start project
This commit is contained in:
commit
ca3c00b6a1
42 changed files with 1310 additions and 0 deletions
7
.env.example
Normal file
7
.env.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
KC_URL="https://keycloak-server.com"
|
||||
KC_REALMS="your-realm"
|
||||
|
||||
VITE_API_URI_CONFIG="https://api-server/api/v1"
|
||||
VITE_CLIENTID_KEYCLOAK="your-client-id"
|
||||
|
||||
SSO_COOKIE_NAME="sso"
|
||||
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
package-lock.json
|
||||
30
README.md
Normal file
30
README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# HRMS SSO
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
npm run preview
|
||||
```
|
||||
|
||||
### Format and fixes files
|
||||
|
||||
```
|
||||
npm run format
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
8
cypress.config.ts
Normal file
8
cypress.config.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
specPattern: 'cypress/e2e/**/*.{cy,spec}.{js,jsx,ts,tsx}',
|
||||
baseUrl: 'http://localhost:4173',
|
||||
},
|
||||
})
|
||||
19
docker/Dockerfile
Normal file
19
docker/Dockerfile
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# docker buildx build --platform=linux/amd64 -f docker/Dockerfile . -t hrms-git.chin.in.th/bma-hrms/hrms-checkin:0.1
|
||||
|
||||
# Build Stage
|
||||
FROM node:20-alpine as build-stage
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY ./ .
|
||||
RUN npm run build
|
||||
|
||||
# Production Stage
|
||||
FROM nginx:stable-alpine AS production-stage
|
||||
RUN mkdir /app
|
||||
COPY --from=build-stage /app/dist /app
|
||||
COPY docker/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
RUN chmod u+x /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
18
docker/entrypoint.sh
Normal file
18
docker/entrypoint.sh
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
ROOT_DIR=/app
|
||||
|
||||
# Replace env vars in JavaScript files
|
||||
echo "Replacing env constants in JS"
|
||||
for file in $ROOT_DIR/assets/app.*.js* $ROOT_DIR/js/app.*.js* $ROOT_DIR/index.html $ROOT_DIR/precache-manifest*.js $ROOT_DIR/assets/index*.js*;
|
||||
do
|
||||
echo "Processing $file ...";
|
||||
|
||||
sed -i 's|VITE_API_URI_CONFIG|'${VITE_API_URI_CONFIG}'|g' $file
|
||||
sed -i 's|VITE_URL_SSO|'${VITE_URL_SSO}'|g' $file
|
||||
|
||||
done
|
||||
|
||||
echo "Starting Nginx"
|
||||
nginx -g 'daemon off;'
|
||||
|
||||
30
docker/nginx.conf
Normal file
30
docker/nginx.conf
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
user nginx;
|
||||
worker_processes 1;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location / {
|
||||
root /app;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
env.d.ts
vendored
Normal file
1
env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
34
index.html
Normal file
34
index.html
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>HRMS SSO</title>
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#viewDiv {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
53
package.json
Normal file
53
package.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"name": "hrms-sso",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p build-only",
|
||||
"preview": "vite preview --port 3002",
|
||||
"build-only": "vite build",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier ./src --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@arcgis/core": "^4.28.10",
|
||||
"@quasar/extras": "^1.15.8",
|
||||
"@vuepic/vue-datepicker": "^5.2.1",
|
||||
"keycloak-js": "^22.0.2",
|
||||
"moment": "^2.29.4",
|
||||
"pinia": "^2.1.4",
|
||||
"quasar": "^2.17.5",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"simple-vue-camera": "^1.1.3",
|
||||
"vite-plugin-pwa": "^0.16.7",
|
||||
"vue": "^3.4.15",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/vite-plugin": "^1.3.0",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/node": "^18.18.10",
|
||||
"@types/vue-router": "^2.0.0",
|
||||
"@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",
|
||||
"cypress": "^12.0.2",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"jsdom": "^20.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.32.12",
|
||||
"start-server-and-test": "^1.15.2",
|
||||
"typescript": "~4.7.4",
|
||||
"vite": "^4.0.0",
|
||||
"vitest": "^0.25.6",
|
||||
"vue-tsc": "^1.0.12"
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
26
src/App.vue
Normal file
26
src/App.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
7
src/api/api.org.ts
Normal file
7
src/api/api.org.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import env from "./index";
|
||||
const org = `${env.API_URI}/org`;
|
||||
// const log = `${env.API_URI}/log`;
|
||||
|
||||
export default {
|
||||
org,
|
||||
};
|
||||
29
src/api/index.ts
Normal file
29
src/api/index.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**config api */
|
||||
import { ref } from 'vue'
|
||||
|
||||
const env = ref<string>(process.env.NODE_ENV || 'development')
|
||||
export const apiUrlConfig = import.meta.env.VITE_API_URI_CONFIG
|
||||
// if (process.env.VUE_APP_TEST) {
|
||||
// env = "test";
|
||||
// }
|
||||
|
||||
const config = ref<any>({
|
||||
development: {
|
||||
// API_URI: "https://localhost:7260/api",
|
||||
API_URI: 'https://bma-ehr.frappet.synology.me/api/v1',
|
||||
},
|
||||
test: {
|
||||
API_URI: 'http://localhost:5010/api/v1',
|
||||
},
|
||||
production: {
|
||||
API_URI: apiUrlConfig,
|
||||
},
|
||||
})
|
||||
|
||||
const API_URI = ref<string>(config.value[env.value].API_URI)
|
||||
|
||||
export default {
|
||||
env: env.value,
|
||||
config: config.value,
|
||||
API_URI: API_URI.value,
|
||||
}
|
||||
13
src/app.config.ts
Normal file
13
src/app.config.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/**ใช้รวมไฟล์ย่อยๆ ของ api แต่ละไฟล์ */
|
||||
|
||||
/** API ระบบลงเวลา */
|
||||
import org from "@/api/api.org";
|
||||
|
||||
const API = {
|
||||
/**message */
|
||||
...org,
|
||||
};
|
||||
|
||||
export default {
|
||||
API: API,
|
||||
};
|
||||
51
src/assets/key/BMA
Normal file
51
src/assets/key/BMA
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
1
src/assets/key/BMA.pub
Normal file
1
src/assets/key/BMA.pub
Normal file
|
|
@ -0,0 +1 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQClVi9NES4zetZ5pvbr40/VWDu3xjv2298+EySGqFG/YTh9a6pyzIaxrNHPuZvQH2p79B2xE2HmuAqTLFQhRhUiFKBaXa4TONXNcF6++by7sCSDWr5aTtI8gR/gaLHEwh81ZDB5ukVeGd2DtZxBHPzoBBQvKQuEkUP1qJsNRBBNYuSRq6dXEvyv2IK4RaPomWEdflqjV+6BA+A6GtOq3dc9sRSrLFpMaVZMY38fZPEOM8fBJRZdHfv7ktW1H6klEqKt8kOtjXCvjRKlNUn6HX3X+V6NYu/cJdZLFj2hWdgE/c/mP7gqrRnsS3WC8HL7eY6wFOu8PxPQicF/R44sPz3pDizv2mRT3Tl7G3bgVd8OlLxk3SuLY5WguZz7LzvP9N+lOBl29sHOt2wZeeZit+t7j/8N+KC1i7J2gplGn0CobekjSy8Y1VzCWZN+EKXstxLpe7ajxamw2BC4nc+CbsAD4U6sgJ7qiu4xfKjgNo2YYrG3ySVN+qMYDzt0P5P+Z5XeFDfwohcPvnQkRtsNXNKT7JEiDrqZGDx/qg4ePmqrXYi/kJ/ufclA0HjMSy8+kFZSrezquK7Fa2slADSh895sQS5W6D95LLYs4PkeFXNyq0L2FuJ2DyAF4Cc+ojSguksFQmdE46Fu0Qut2EgsHESV7CHaXi3bG8Yb2GkC+M+nTQ== waruneeta@AUYs-MacBook-Pro.local
|
||||
14
src/assets/key/BMA.pub.pem
Normal file
14
src/assets/key/BMA.pub.pem
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApVYvTREuM3rWeab26+NP
|
||||
1Vg7t8Y79tvfPhMkhqhRv2E4fWuqcsyGsazRz7mb0B9qe/QdsRNh5rgKkyxUIUYV
|
||||
IhSgWl2uEzjVzXBevvm8u7Akg1q+Wk7SPIEf4GixxMIfNWQwebpFXhndg7WcQRz8
|
||||
6AQULykLhJFD9aibDUQQTWLkkaunVxL8r9iCuEWj6JlhHX5ao1fugQPgOhrTqt3X
|
||||
PbEUqyxaTGlWTGN/H2TxDjPHwSUWXR37+5LVtR+pJRKirfJDrY1wr40SpTVJ+h19
|
||||
1/lejWLv3CXWSxY9oVnYBP3P5j+4Kq0Z7Et1gvBy+3mOsBTrvD8T0InBf0eOLD89
|
||||
6Q4s79pkU905ext24FXfDpS8ZN0ri2OVoLmc+y87z/TfpTgZdvbBzrdsGXnmYrfr
|
||||
e4//DfigtYuydoKZRp9AqG3pI0svGNVcwlmTfhCl7LcS6Xu2o8WpsNgQuJ3Pgm7A
|
||||
A+FOrICe6oruMXyo4DaNmGKxt8klTfqjGA87dD+T/meV3hQ38KIXD750JEbbDVzS
|
||||
k+yRIg66mRg8f6oOHj5qq12Iv5Cf7n3JQNB4zEsvPpBWUq3s6riuxWtrJQA0ofPe
|
||||
bEEuVug/eSy2LOD5HhVzcqtC9hbidg8gBeAnPqI0oLpLBUJnROOhbtELrdhILBxE
|
||||
lewh2l4t2xvGG9hpAvjPp00CAwEAAQ==
|
||||
-----END PUBLIC KEY-----
|
||||
BIN
src/assets/line.png
Normal file
BIN
src/assets/line.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/sso.png
Normal file
BIN
src/assets/sso.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
57
src/interface/index/Main.ts
Normal file
57
src/interface/index/Main.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
interface DataOption {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
interface DataDateMonthObject {
|
||||
month: number
|
||||
year: number
|
||||
}
|
||||
interface FormRef {
|
||||
date: object | null
|
||||
reason: object | null
|
||||
[key: string]: any
|
||||
}
|
||||
interface notiType {
|
||||
id: string
|
||||
sender: string
|
||||
body: string
|
||||
timereceive: Date
|
||||
isOpen: boolean
|
||||
}
|
||||
|
||||
interface LocationObject {
|
||||
latitude: number
|
||||
longitude: number
|
||||
}
|
||||
|
||||
interface Pagination {
|
||||
sortBy: string | null
|
||||
descending: boolean
|
||||
page: number
|
||||
rowsPerPage: number | undefined
|
||||
}
|
||||
interface DataCheckIn {
|
||||
checkInDate: string
|
||||
checkInDateTime: string
|
||||
checkInId: string
|
||||
checkInLocation: string
|
||||
checkInStatus: string
|
||||
checkInTime: string
|
||||
checkOutLocation: string
|
||||
checkOutStatus: string
|
||||
checkOutTime: string
|
||||
editReason: string
|
||||
editStatus: string
|
||||
isEdit: boolean
|
||||
}
|
||||
|
||||
export type {
|
||||
DataOption,
|
||||
FormRef,
|
||||
notiType,
|
||||
DataDateMonthObject,
|
||||
LocationObject,
|
||||
Pagination,
|
||||
DataCheckIn,
|
||||
}
|
||||
13
src/interface/response/Main.ts
Normal file
13
src/interface/response/Main.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
interface Noti {
|
||||
id: string
|
||||
body: string
|
||||
receiverUserId: string
|
||||
type: string
|
||||
payload: null
|
||||
isOpen: false
|
||||
receiveDate: Date
|
||||
openDate: null
|
||||
createdFullName: string
|
||||
}
|
||||
|
||||
export type { Noti }
|
||||
38
src/main.ts
Normal file
38
src/main.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { createApp, defineAsyncComponent } from 'vue'
|
||||
import App from '@/App.vue'
|
||||
import '@/registerServiceWorker'
|
||||
import router from '@/router'
|
||||
import { createPinia } from 'pinia'
|
||||
import { Quasar, Dialog, Notify, Loading } from 'quasar'
|
||||
import '@vuepic/vue-datepicker/dist/main.css'
|
||||
import quasarUserOptions from '@/quasar-user-options'
|
||||
|
||||
import 'quasar/src/css/index.sass'
|
||||
import th from 'quasar/lang/th'
|
||||
|
||||
import http from '@/plugins/http'
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
|
||||
app.use(Quasar, {
|
||||
...quasarUserOptions,
|
||||
plugins: {
|
||||
Notify,
|
||||
Dialog,
|
||||
Loading,
|
||||
},
|
||||
lang: th,
|
||||
})
|
||||
|
||||
app.component(
|
||||
'datepicker',
|
||||
defineAsyncComponent(() => import('@vuepic/vue-datepicker'))
|
||||
)
|
||||
|
||||
app.config.globalProperties.$http = http
|
||||
|
||||
app.mount('#app')
|
||||
24
src/plugins/axios.ts
Normal file
24
src/plugins/axios.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import axios from 'axios'
|
||||
// import { dotnetPath } from "../path/axiosPath";
|
||||
// import { getToken } from "@baloise/vue-keycloak";
|
||||
import { getToken } from './auth'
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
withCredentials: false,
|
||||
})
|
||||
|
||||
// axiosInstance.defaults.baseURL = dotnetPath;
|
||||
axiosInstance.interceptors.request.use(
|
||||
async (config) => {
|
||||
const token = await getToken()
|
||||
config.headers = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
}
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default axiosInstance
|
||||
20
src/plugins/filters.ts
Normal file
20
src/plugins/filters.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* GLOABL Filters
|
||||
* - ไฟล์นี้จะไว้เก็บฟังก์ชันง่าย ๆ พวก Helper Functions ทั้งหลาย
|
||||
*/
|
||||
|
||||
const filters = {
|
||||
/**
|
||||
* ฟังก์ชัน compactNumber ใช้แปลงตัวเลขยาว ๆ ให้กลายเป็นเลขสั้น ๆ แบบที่พวก Social Media ชอบใช้กัน เช่น 1,000 แปลงเป็น 1K หรือ 1,000,000 แปลงเป็น 1M
|
||||
* วิธีใช้ : {{ $filters.compactNumber(value) }}
|
||||
*
|
||||
* @param val รับค่าพารามิเตอร์เป็นตัวแปรชนิดตัวเลข
|
||||
* @returns คืนค่าเป็นตัวเลขที่แปลงค่าแล้ว
|
||||
*/
|
||||
compactNumber(val: number) {
|
||||
const formatter = Intl.NumberFormat('en', { notation: 'compact' })
|
||||
return formatter.format(val)
|
||||
},
|
||||
}
|
||||
|
||||
export default filters
|
||||
39
src/plugins/http.ts
Normal file
39
src/plugins/http.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import Axios, { type AxiosRequestConfig, type AxiosResponse } from "axios";
|
||||
|
||||
const http = Axios.create({
|
||||
timeout: 1000000000, // เพิ่มค่า timeout
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
},
|
||||
});
|
||||
|
||||
http.interceptors.request.use(
|
||||
async function (config: AxiosRequestConfig<any>) {
|
||||
config.headers = config.headers ?? {};
|
||||
|
||||
return config;
|
||||
},
|
||||
function (error: any) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
http.interceptors.response.use(
|
||||
function (response: AxiosResponse<any, any>) {
|
||||
return response;
|
||||
},
|
||||
function (error: any) {
|
||||
if (typeof error !== undefined) {
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (error.hasOwnProperty("response")) {
|
||||
if (error.response.status === 401 || error.response.status === 403) {
|
||||
// Store.commit("SET_ERROR_MESSAGE", error.response.data.message);
|
||||
// Store.commit("REMOVE_ACCESS_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default http;
|
||||
70
src/plugins/keycloak.ts
Normal file
70
src/plugins/keycloak.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// authen with keycloak client
|
||||
import Keycloak from "keycloak-js";
|
||||
|
||||
// const ACCESS_TOKEN = 'BMAHRIS_KEYCLOAK_IDENTITY'
|
||||
// const REFRESH_TOKEN = 'BMAHRIS_KEYCLOAK_REFRESH'
|
||||
// const keycloakConfig = {
|
||||
// url: import.meta.env.VITE_URL_KEYCLOAK,
|
||||
// realm: import.meta.env.VITE_REALM_KEYCLOAK,
|
||||
// clientId: import.meta.env.VITE_CLIENTID_KEYCLOAK,
|
||||
// clientSecret: import.meta.env.VITE_CLIENTSECRET_KEYCLOAK,
|
||||
// }
|
||||
|
||||
// const keycloak = new Keycloak(keycloakConfig)
|
||||
|
||||
// async function kcAuthen(access_token: string, refresh_token: string) {
|
||||
// await setCookie(ACCESS_TOKEN, access_token, 1)
|
||||
// await setCookie(REFRESH_TOKEN, refresh_token, 1)
|
||||
// window.location.href = '/'
|
||||
// }
|
||||
|
||||
// async function kcLogout() {
|
||||
// await deleteCookie(ACCESS_TOKEN)
|
||||
// await deleteCookie(REFRESH_TOKEN)
|
||||
// if (keycloak.authenticated !== undefined) {
|
||||
// keycloak.logout()
|
||||
// }
|
||||
// window.location.href = '/login'
|
||||
// }
|
||||
|
||||
// async function getToken() {
|
||||
// return {
|
||||
// token: getCookie(ACCESS_TOKEN),
|
||||
// refresh_token: getCookie(REFRESH_TOKEN),
|
||||
// }
|
||||
// }
|
||||
|
||||
// function setCookie(name: string, value: any, days: number) {
|
||||
// let expires = ''
|
||||
// if (days) {
|
||||
// const date = new Date()
|
||||
// date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)
|
||||
// expires = '; expires=' + date.toUTCString()
|
||||
// }
|
||||
// document.cookie = name + '=' + (value || '') + expires + '; path=/'
|
||||
// }
|
||||
|
||||
// function getCookie(name: string) {
|
||||
// const nameEQ = name + '='
|
||||
// const ca = document.cookie.split(';')
|
||||
// for (let i = 0; i < ca.length; i++) {
|
||||
// let c = ca[i]
|
||||
// while (c.charAt(0) == ' ') c = c.substring(1, c.length)
|
||||
// if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length)
|
||||
// }
|
||||
// return null
|
||||
// }
|
||||
|
||||
// function deleteCookie(name: string) {
|
||||
// document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
|
||||
// }
|
||||
|
||||
// export default keycloak
|
||||
// export {
|
||||
// keycloakConfig,
|
||||
// getToken,
|
||||
// kcAuthen,
|
||||
// kcLogout,
|
||||
// ACCESS_TOKEN,
|
||||
// REFRESH_TOKEN,
|
||||
// }
|
||||
11
src/quasar-user-options.ts
Normal file
11
src/quasar-user-options.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// import "./styles/quasar.scss"
|
||||
import '@quasar/extras/material-icons/material-icons.css'
|
||||
import '@quasar/extras/material-icons-outlined/material-icons-outlined.css'
|
||||
import '@quasar/extras/fontawesome-v5/fontawesome-v5.css'
|
||||
import '@quasar/extras/mdi-v4/mdi-v4.css'
|
||||
|
||||
// To be used on app.use(Quasar, { ... })
|
||||
export default {
|
||||
config: {},
|
||||
plugins: {},
|
||||
}
|
||||
34
src/registerServiceWorker.ts
Normal file
34
src/registerServiceWorker.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* eslint-disable no-console */
|
||||
|
||||
import { register } from 'register-service-worker'
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register('registerSW.js', {
|
||||
ready() {
|
||||
console.log(
|
||||
'App is being served from cache by a service worker.\n' +
|
||||
'For more details, visit https://goo.gl/AFskqB'
|
||||
)
|
||||
},
|
||||
registered() {
|
||||
console.log('Service worker has been registered.')
|
||||
},
|
||||
cached() {
|
||||
console.log('Content has been cached for offline use.')
|
||||
},
|
||||
updatefound() {
|
||||
console.log('New content is downloading.')
|
||||
},
|
||||
updated() {
|
||||
console.log('New content is available; please refresh.')
|
||||
},
|
||||
offline() {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
)
|
||||
},
|
||||
error(error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
},
|
||||
})
|
||||
}
|
||||
29
src/router/index.ts
Normal file
29
src/router/index.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { createRouter, createWebHistory } from "vue-router";
|
||||
const loginView = () => import("@/views/login.vue");
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "loginMain",
|
||||
component: loginView,
|
||||
children: [
|
||||
/**
|
||||
* 404 Not Found
|
||||
* ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route
|
||||
*/
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
name: "NotFound",
|
||||
component: () => import("@/views/ErrorNotFoundPage.vue"),
|
||||
meta: {
|
||||
Auth: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
||||
1
src/shims-vue.d.ts
vendored
Normal file
1
src/shims-vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
declare module '*.vue'
|
||||
13
src/stores/main.ts
Normal file
13
src/stores/main.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { useCounterMixin } from '@/stores/mixin'
|
||||
|
||||
const mixin = useCounterMixin()
|
||||
|
||||
/** store for checkin history*/
|
||||
export const useSsoHrms = defineStore('hrmssso', () => {
|
||||
|
||||
|
||||
return {
|
||||
|
||||
}
|
||||
})
|
||||
91
src/stores/mixin.ts
Normal file
91
src/stores/mixin.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { Loading, QSpinnerCube } from "quasar";
|
||||
|
||||
export const useCounterMixin = defineStore("mixin", () => {
|
||||
function date2Thai(srcDate: Date, isFullMonth = false, isTime = false) {
|
||||
if (srcDate == null) {
|
||||
return null;
|
||||
`
|
||||
`;
|
||||
}
|
||||
const date = new Date(srcDate);
|
||||
const isValidDate = Boolean(+date);
|
||||
if (!isValidDate) return srcDate.toString();
|
||||
if (isValidDate && date.getFullYear() < 1000) return srcDate.toString();
|
||||
const fullMonthThai = [
|
||||
"มกราคม",
|
||||
"กุมภาพันธ์",
|
||||
"มีนาคม",
|
||||
"เมษายน",
|
||||
"พฤษภาคม",
|
||||
"มิถุนายน",
|
||||
"กรกฎาคม",
|
||||
"สิงหาคม",
|
||||
"กันยายน",
|
||||
"ตุลาคม",
|
||||
"พฤศจิกายน",
|
||||
"ธันวาคม",
|
||||
];
|
||||
const abbrMonthThai = [
|
||||
"ม.ค.",
|
||||
"ก.พ.",
|
||||
"มี.ค.",
|
||||
"เม.ย.",
|
||||
"พ.ค.",
|
||||
"มิ.ย.",
|
||||
"ก.ค.",
|
||||
"ส.ค.",
|
||||
"ก.ย.",
|
||||
"ต.ค.",
|
||||
"พ.ย.",
|
||||
"ธ.ค.",
|
||||
];
|
||||
let dstYear = 0;
|
||||
if (date.getFullYear() > 2500) {
|
||||
dstYear = date.getFullYear();
|
||||
} else {
|
||||
dstYear = date.getFullYear() + 543;
|
||||
}
|
||||
let dstMonth = "";
|
||||
if (isFullMonth) {
|
||||
dstMonth = fullMonthThai[date.getMonth()];
|
||||
} else {
|
||||
dstMonth = abbrMonthThai[date.getMonth()];
|
||||
}
|
||||
let dstTime = "";
|
||||
if (isTime) {
|
||||
const H = date.getHours().toString().padStart(2, "0");
|
||||
const M = date.getMinutes().toString().padStart(2, "0");
|
||||
// const S = date.getSeconds().toString().padStart(2, "0")
|
||||
// dstTime = " " + H + ":" + M + ":" + S + " น."
|
||||
dstTime = " " + H + ":" + M + " น.";
|
||||
}
|
||||
return (
|
||||
date.getDate().toString().padStart(2, "0") +
|
||||
" " +
|
||||
dstMonth +
|
||||
" " +
|
||||
dstYear +
|
||||
dstTime
|
||||
);
|
||||
}
|
||||
|
||||
const showLoader = () => {
|
||||
Loading.show({
|
||||
spinner: QSpinnerCube,
|
||||
spinnerSize: 140,
|
||||
spinnerColor: "primary",
|
||||
backgroundColor: "white",
|
||||
});
|
||||
};
|
||||
|
||||
const hideLoader = () => {
|
||||
Loading.hide();
|
||||
};
|
||||
|
||||
return {
|
||||
date2Thai,
|
||||
showLoader,
|
||||
hideLoader,
|
||||
};
|
||||
});
|
||||
139
src/style/quasar-variables.sass
Normal file
139
src/style/quasar-variables.sass
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// FILE (create it): src/quasar-variables.sass
|
||||
|
||||
$primary: #02A998
|
||||
$secondary: #016987
|
||||
$accent: #9C27B0
|
||||
|
||||
// $dark: #1D1D1D
|
||||
$dark: #35473C
|
||||
|
||||
$positive: #21BA45
|
||||
$negative: #C10015
|
||||
$info: #31CCEC
|
||||
$warning: #F2C037
|
||||
|
||||
$add: #00aa86
|
||||
.text-add
|
||||
color: $add !important
|
||||
.bg-add
|
||||
background: $add !important
|
||||
|
||||
$edit: #019fc4
|
||||
.text-edit
|
||||
color: $edit !important
|
||||
.bg-edit
|
||||
background: $edit !important
|
||||
|
||||
$public: #016987
|
||||
.text-public
|
||||
color: $public !important
|
||||
.bg-public
|
||||
background: $public !important
|
||||
|
||||
$save: #4154b3
|
||||
.text-save
|
||||
color: $save !important
|
||||
.bg-save
|
||||
background: $save !important
|
||||
|
||||
$nativetab: #c8d3db
|
||||
.text-nativetab
|
||||
color: $nativetab !important
|
||||
.bg-nativetab
|
||||
background: $nativetab !important
|
||||
|
||||
$activetab: #4a5568
|
||||
.text-activetab
|
||||
color: $activetab !important
|
||||
.bg-activetab
|
||||
background: $activetab !important
|
||||
|
||||
.inputgreen .q-field__prefix,
|
||||
.inputgreen .q-field__suffix,
|
||||
.inputgreen .q-field__input,
|
||||
.inputgreen .q-field__native
|
||||
|
||||
color: rgb(6, 136, 77)
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@100;200;300;400;500;600;700;800;900&display=swap')
|
||||
|
||||
$noto-thai: 'Noto Sans Thai', sans-serif
|
||||
|
||||
#azay-app,
|
||||
div
|
||||
font-family: $noto-thai !important
|
||||
text-rendering: optimizeLegibility
|
||||
-webkit-font-smoothing: antialiased
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
|
||||
$separator-color: #EDEDED !default
|
||||
|
||||
.bg-teal-1
|
||||
background: #e0f2f1a6 !important
|
||||
|
||||
.table_ellipsis
|
||||
max-width: 200px
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.table_ellipsis:hover
|
||||
word-wrap: break-word
|
||||
overflow: visible
|
||||
white-space: normal
|
||||
|
||||
.table_ellipsis2
|
||||
max-width: 25vw
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.table_ellipsis2:hover
|
||||
word-wrap: break-word
|
||||
overflow: visible
|
||||
white-space: normal
|
||||
transition: width 2s
|
||||
|
||||
$muti-tab: #87d4cc
|
||||
.text-muti-tab
|
||||
color: $muti-tab !important
|
||||
.bg-muti-tab
|
||||
background: $muti-tab !important
|
||||
|
||||
|
||||
/* editor */
|
||||
|
||||
.q-editor
|
||||
font-size: 1rem
|
||||
line-height: 1.5rem
|
||||
font-weight: 400
|
||||
|
||||
.q-editor h1, .q-menu h1
|
||||
font-size: 1.5rem
|
||||
line-height: 2rem
|
||||
font-weight: 400
|
||||
margin-block-start: 0em
|
||||
margin-block-end: 0em
|
||||
|
||||
.q-editor h2, .q-menu h2
|
||||
font-size: 1.25rem
|
||||
line-height: 1.5rem
|
||||
font-weight: 400
|
||||
margin-block-start: 0em
|
||||
margin-block-end: 0em
|
||||
|
||||
|
||||
.q-editor h3, .q-menu h3
|
||||
font-size: 1.1rem
|
||||
line-height: 1.5rem
|
||||
font-weight: 400
|
||||
margin-block-start: 0em
|
||||
margin-block-end: 0em
|
||||
|
||||
.q-editor p, .q-menu p
|
||||
margin: 0
|
||||
|
||||
/* q-tree */
|
||||
|
||||
.q-tree
|
||||
color: #c8d3db
|
||||
27
src/views/ErrorNotFoundPage.vue
Normal file
27
src/views/ErrorNotFoundPage.vue
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Error404NotFound',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fullscreen bg-secondary text-white text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div>
|
||||
<div class="text-h1">ไม่พบหน้าที่ต้องการ</div>
|
||||
<div class="text-h2">(404 Page Not Found)</div>
|
||||
<q-btn
|
||||
class="q-mt-xl"
|
||||
color="white"
|
||||
text-color="secondary"
|
||||
unelevated
|
||||
to="/"
|
||||
label="กลับไปหน้าหลัก"
|
||||
no-caps
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
210
src/views/login.vue
Normal file
210
src/views/login.vue
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
<!-- authen with keycloak client -->
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import axios from "axios";
|
||||
import { useCounterMixin } from "@/stores/mixin";
|
||||
import config from "@/app.config";
|
||||
|
||||
const mixin = useCounterMixin();
|
||||
|
||||
const { showLoader, hideLoader } = mixin;
|
||||
|
||||
const msgError = ref<string>("");
|
||||
const username = ref<string>("");
|
||||
const password = ref<string>("");
|
||||
async function onSubmit() {
|
||||
showLoader();
|
||||
const formdata = new URLSearchParams();
|
||||
formdata.append("username", username.value);
|
||||
formdata.append("password", password.value);
|
||||
|
||||
await axios
|
||||
.post(`${config.API.org}/login`, formdata, {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
})
|
||||
.then(async (res) => {
|
||||
// setAuthen(res.data.result);
|
||||
})
|
||||
.catch((err) => {
|
||||
// $q.dialog({
|
||||
// component: CustomComponent,
|
||||
// componentProps: {
|
||||
// title: `ข้อความแจ้งเตือน`,
|
||||
// message: `${err.response.data.message}`,
|
||||
// icon: "warning",
|
||||
// color: "red",
|
||||
// onlycancel: true,
|
||||
// },
|
||||
// });
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoader();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// const checkAuthen = await authenticated();
|
||||
// if (checkAuthen) {
|
||||
// router.push("/");
|
||||
// }
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="vh-100" style="overflow: hidden">
|
||||
<div class="container-fluid h-custom">
|
||||
<div class="row d-flex justify-content-center align-items-center h-100">
|
||||
<!-- 1 sec -->
|
||||
<div
|
||||
class="col-md-12 col-lg-6 bg-img d-none d-lg-block position-relative"
|
||||
>
|
||||
<img src="@/assets/line.png" class="img_absolute_line" />
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center vh-100"
|
||||
>
|
||||
<div class="text-white position-relative">
|
||||
<img src="@/assets/sso.png" class="img_absolute" />
|
||||
<h4>ระบบบริหารจัดการการใช้งาน</h4>
|
||||
<h4 class="pb-2">ระบบสารสนเทศสนับสนุนการเชื่อมโยง</h4>
|
||||
<p class="mb-0 txt_detail">Bangkok Metropolitan Administration</p>
|
||||
<p class="mb-0 txt_detail">Single Sign-On</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2 sec -->
|
||||
|
||||
<div class="col-10 col-md-8 col-lg-5 offset-lg-1">
|
||||
<form class="bg_md" id="contact-form">
|
||||
<div class="d_over">
|
||||
<div class="text-center">
|
||||
<img src="@/assets/sso.png" />
|
||||
</div>
|
||||
<div class="my-3 text-center" style="color: #00aa86">
|
||||
<h5>ระบบบริหารจัดการการใช้งาน</h5>
|
||||
<h5 class="pb-1">ระบบสารสนเทศสนับสนุนการเชื่อมโยง</h5>
|
||||
<p class="mb-0 txt_detail">
|
||||
Bangkok Metropolitan Administration
|
||||
</p>
|
||||
<p class="mb-0 txt_detail">Single Sign-On</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex flex-row align-items-center justify-content-center justify-content-lg-start"
|
||||
>
|
||||
<p class="lead fw-normal mb-0 me-3 mb-2">
|
||||
<strong>เข้าใช้งานระบบ</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-12 col-lg-9">
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
value=""
|
||||
class="form-control form-control-lg"
|
||||
placeholder="ชื่อผู้ใช้"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-12 col-lg-9">
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value=""
|
||||
class="form-control form-control-lg"
|
||||
placeholder="รหัสผ่าน"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-lg-9 d-grid">
|
||||
<button type="submit" class="btn_custom">เข้าระบบ</button>
|
||||
<div id="response-message">{{ msgError }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.img_absolute_line {
|
||||
position: absolute;
|
||||
height: auto;
|
||||
width: 50%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.txt_detail {
|
||||
color: #c0c0c0;
|
||||
}
|
||||
.img_absolute {
|
||||
position: absolute;
|
||||
top: -170px;
|
||||
}
|
||||
.d_over {
|
||||
display: none;
|
||||
}
|
||||
.btn_custom {
|
||||
background-color: #00aa86;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
padding: 8px 0 8px 0;
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn_custom:hover {
|
||||
background-color: #00ca9f;
|
||||
}
|
||||
.bg-img {
|
||||
background: rgb(30, 50, 49);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(30, 50, 49, 1) 0%,
|
||||
rgba(20, 120, 99, 1) 100%
|
||||
);
|
||||
}
|
||||
body {
|
||||
font-family: "Noto Sans Thai", sans-serif !important;
|
||||
}
|
||||
.h-custom {
|
||||
height: calc(100% - 73px);
|
||||
}
|
||||
@media (max-width: 450px) {
|
||||
.h-custom {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
/* Up to medium screens */
|
||||
section.vh-100 {
|
||||
background: rgb(30, 50, 49);
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(30, 50, 49, 1) 0%,
|
||||
rgba(20, 120, 99, 1) 100%
|
||||
);
|
||||
}
|
||||
.bg_md {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.d_over {
|
||||
display: block;
|
||||
}
|
||||
.txt_detail {
|
||||
color: #949494;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
#response-message {
|
||||
color: red;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
16
tsconfig.app.json
Normal file
16
tsconfig.app.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"ignoreDeprecations": "5.0",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["dom", "es2015", "es2018", "es2018.promise"]
|
||||
}
|
||||
}
|
||||
16
tsconfig.config.json
Normal file
16
tsconfig.config.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"],
|
||||
"ignoreDeprecations": "5.0",
|
||||
"verbatimModuleSyntax": true,
|
||||
"module": "esnext"
|
||||
}
|
||||
}
|
||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.vitest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
tsconfig.vitest.json
Normal file
9
tsconfig.vitest.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "./tsconfig.app.json",
|
||||
"exclude": [],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"lib": [],
|
||||
"types": ["node", "jsdom"]
|
||||
}
|
||||
}
|
||||
66
vite.config.js
Normal file
66
vite.config.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue({
|
||||
template: { transformAssetUrls },
|
||||
}),
|
||||
quasar({
|
||||
sassVariables: 'src/style/quasar-variables.sass',
|
||||
}),
|
||||
vueJsx(),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: 'auto',
|
||||
workbox: {
|
||||
cleanupOutdatedCaches: true,
|
||||
globPatterns: ['**/*.*'],
|
||||
},
|
||||
includeAssets: ['icons/safari-pinned-tab.svg'],
|
||||
manifest: {
|
||||
name: 'BMA-Checkin',
|
||||
short_name: 'EHR Checkin',
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: 'icons/android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'icons/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: ['any', 'maskable'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
server: {
|
||||
port: 3002,
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['esri-loader'],
|
||||
},
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue