diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b82d800 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +**/node_modules +**/dist +Dockerfile +docker-compose.yaml + diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..802c160 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,23 @@ +name: deploy-docker +run-name: deploy-docker ${{ github.actor }} +on: + workflow_dispatch: +jobs: + # https://github.com/appleboy/ssh-action + # act -W .github/workflows/deploy.yaml -j remote-deploy -s SSH_PASSWORD + remote-deploy: + runs-on: ubuntu-latest + steps: + - name: Remote Deployment + uses: appleboy/ssh-action@v0.1.8 + with: + host: frappet.com + username: frappet + password: ${{ secrets.SSH_PASSWORD }} + port: 22 + script: | + cd /home/frappet/docker/bma-ehr-recruit-qualifying-exam + docker-compose pull + docker-compose up -d + touch success + diff --git a/.github/workflows/local-build.yaml b/.github/workflows/local-build.yaml new file mode 100644 index 0000000..8c799b5 --- /dev/null +++ b/.github/workflows/local-build.yaml @@ -0,0 +1,34 @@ + +name: local-build +run-name: local-build ${{ github.actor }} +on: + # push: + # tags: + # - v1.** + # branches: + # - 'main' + # branches: + # - 'release-*' + + # Allow run workflow manually from Action tab + workflow_dispatch: +env: + REGISTRY: docker.frappet.com + CMS_IMAGE_NAME: demo/qualifying-exam-cms + CMS_IMAGE_TAG: 0.1.1 +jobs: + # act -W .github/workflows/local-build.yaml -j local-image + local-image: + runs-on: ubuntu-latest + steps: + - name: "Check out code" + uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Build and push docker image + uses: docker/build-push-action@v3 + with: + context: cms + load: true + tags: ${{env.REGISTRY}}/${{env.CMS_IMAGE_NAME}}:${{env.CMS_IMAGE_TAG}},${{env.REGISTRY}}/${{env.CMS_IMAGE_NAME}}:latest diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..6012f49 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,72 @@ +name: release +run-name: release ${{ github.actor }} +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + tags-ignore: + - '2.*' + # Allow run workflow manually from Action tab + workflow_dispatch: + inputs: + IMAGE_VER: + description: "version for build image" + type: string +env: + REGISTRY: docker.frappet.com + IMAGE_NAME: demo/test-fe-exam +jobs: + # act workflow_dispatch -W .github/workflows/release.yaml --input IMAGE_VER=v0.2.1-dev -s DOCKER_USER=sorawit -s DOCKER_PASS=P@ssword -s SSH_PASSWORD=P@ssw0rd + release: + runs-on: ubuntu-latest + steps: + # https://thekevinwang.com/2022/06/13/github-actions-survival-skills/ + - name: Check out code # checkout only cms is possible but I checkout all + uses: actions/checkout@v3 + - name: Gen Version + id: gen_ver + run: | + if [[ $GITHUB_REF == 'refs/tags/'* ]]; then + IMAGE_VER='${GITHUB_REF/refs\/tags\//}' + else + IMAGE_VER=${{ github.event.inputs.IMAGE_VER }} + fi + if [[ $IMAGE_VER == '' ]]; then + IMAGE_VER='beta' + fi + # echo "{\"version\":\"$IMAGE_VER\", \"builddate\":\"$(date +"%Y-%m-%d_%T")\",\"ref_name\":\"$GITHUB_REF\" }" > ./cms/src/lib/ver.json + # cat ./cms/src/lib/ver.json + echo '::set-output name=image_ver::'$IMAGE_VER + - name: Test Version + run: | + echo $GITHUB_REF + echo ${{ steps.gen_ver.outputs.image_ver }} + # cat ./cms/src/lib/ver.json + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login in to registry + uses: docker/login-action@v2 + with: + registry: ${{env.REGISTRY}} + username: ${{secrets.DOCKER_USER}} + password: ${{secrets.DOCKER_PASS}} + - name: Build and push docker image + uses: docker/build-push-action@v3 + with: + # context: cms + # platforms: linux/amd64,linux/arm64 + push: true + tags: ${{env.REGISTRY}}/${{env.IMAGE_NAME}}:${{ steps.gen_ver.outputs.image_ver }},${{env.REGISTRY}}/${{env.IMAGE_NAME}}:latest + # - name: Remote Deployment + # uses: appleboy/ssh-action@v0.1.8 + # with: + # host: frappet.com + # username: frappet + # password: ${{ secrets.SSH_PASSWORD }} + # port: 22 + # script: | + # cd /home/frappet/docker/bma-ehr-recruit-qualifying-exam + # docker-compose pull + # docker-compose up -d + # touch success + diff --git a/.github/workflows/remote-build.yaml b/.github/workflows/remote-build.yaml new file mode 100644 index 0000000..999b900 --- /dev/null +++ b/.github/workflows/remote-build.yaml @@ -0,0 +1,55 @@ +name: remote-build +run-name: remote-build ${{ github.actor }} +on: + # push: + # tags: + # - 'v[0-9]+.[0-9]+.[0-9]+' + # tags-ignore: + # - '2.*' + # Allow run workflow manually from Action tab + workflow_dispatch: +env: + REGISTRY: docker.frappet.com + CMS_IMAGE_NAME: demo/qualifying-exam-cms + CMS_IMAGE_TAG: 0.2.1 +jobs: + # act --workflows .github/workflows/build.yaml --job remote-image -s DOCKER_USER -s DOCKER_PASS -s SSH_PASSWORD + # act -W .github/workflows/remote-build.yaml -j remote-image -s DOCKER_USER -s DOCKER_PASS -s SSH_PASSWORD + remote-image: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + # skip Set up QEMU because it fail on act and container + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login in to registry + uses: docker/login-action@v2 + with: + registry: ${{env.REGISTRY}} + username: ${{secrets.DOCKER_USER}} + password: ${{secrets.DOCKER_PASS}} + # Node no need because use 2 state build + # - uses: actions/setup-node@v3 + # with: + # node-version: '18' + - name: Build and push docker image + uses: docker/build-push-action@v3 + with: + context: cms + # platforms: linux/amd64,linux/arm64 + push: true + tags: ${{env.REGISTRY}}/${{env.CMS_IMAGE_NAME}}:${{env.CMS_IMAGE_TAG}},${{env.REGISTRY}}/${{env.CMS_IMAGE_NAME}}:latest + - name: Remote Deployment + uses: appleboy/ssh-action@v0.1.8 + with: + host: frappet.com + username: frappet + password: ${{ secrets.SSH_PASSWORD }} + port: 22 + script: | + cd /home/frappet/docker/bma-ehr-recruit-qualifying-exam + docker-compose pull + docker-compose up -d + touch success + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5c805e9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +# docker build . -t docker.frappet.com/demo/fe:latest +FROM node:latest as build-stage +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY ./ . +RUN npm run build + +FROM nginx as production-stage +RUN mkdir /app +COPY --from=build-stage /app/dist /app +COPY nginx.conf /etc/nginx/nginx.conf diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..6f61d6c --- /dev/null +++ b/nginx.conf @@ -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; + } + } +} diff --git a/package-lock.json b/package-lock.json index c91b905..4c78509 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0", "dependencies": { "@quasar/extras": "^1.15.11", + "@vuepic/vue-datepicker": "^4.2.1", + "keycloak-js": "^21.0.1", + "moment": "^2.29.4", "pinia": "^2.0.32", "quasar": "^2.11.7", "vue": "^3.2.47", @@ -2031,6 +2034,21 @@ } } }, + "node_modules/@vuepic/vue-datepicker": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-4.2.1.tgz", + "integrity": "sha512-O8hy0o9jQkv/Et7G0mUFDR94NTcHHouy70ELfrTmaWOIBamS/8cRWAwwb/reOvKPX+eo1XKs/v2mj+i5WA14kw==", + "dependencies": { + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.7" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -2354,7 +2372,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -3054,6 +3071,26 @@ "node": ">=14" } }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "peerDependencies": { + "date-fns": ">=2.0.0" + } + }, "node_modules/dayjs": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", @@ -5189,6 +5226,11 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5383,6 +5425,15 @@ "verror": "1.10.0" } }, + "node_modules/keycloak-js": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-21.0.1.tgz", + "integrity": "sha512-ot0KW4qyDHl5AyDZNV0CkEkuvIZi+37y3BReNvqqfag7wqJeV13R/PcgECvbbd05+0NSOQjhBL8S+a4A++vNQw==", + "dependencies": { + "base64-js": "^1.5.1", + "js-sha256": "^0.9.0" + } + }, "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -5801,6 +5852,14 @@ "ufo": "^1.1.1" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9601,6 +9660,15 @@ "dev": true, "requires": {} }, + "@vuepic/vue-datepicker": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-4.2.1.tgz", + "integrity": "sha512-O8hy0o9jQkv/Et7G0mUFDR94NTcHHouy70ELfrTmaWOIBamS/8cRWAwwb/reOvKPX+eo1XKs/v2mj+i5WA14kw==", + "requires": { + "date-fns": "^2.29.3", + "date-fns-tz": "^1.3.7" + } + }, "abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -9842,8 +9910,7 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -10348,6 +10415,17 @@ "whatwg-url": "^12.0.0" } }, + "date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==" + }, + "date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "requires": {} + }, "dayjs": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", @@ -11909,6 +11987,11 @@ "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", "dev": true }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12065,6 +12148,15 @@ "verror": "1.10.0" } }, + "keycloak-js": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-21.0.1.tgz", + "integrity": "sha512-ot0KW4qyDHl5AyDZNV0CkEkuvIZi+37y3BReNvqqfag7wqJeV13R/PcgECvbbd05+0NSOQjhBL8S+a4A++vNQw==", + "requires": { + "base64-js": "^1.5.1", + "js-sha256": "^0.9.0" + } + }, "lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -12379,6 +12471,11 @@ "ufo": "^1.1.1" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/package.json b/package.json index b0875d5..5cfe785 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ }, "dependencies": { "@quasar/extras": "^1.15.11", + "@vuepic/vue-datepicker": "^4.2.1", + "keycloak-js": "^21.0.1", + "moment": "^2.29.4", "pinia": "^2.0.32", "quasar": "^2.11.7", "vue": "^3.2.47", diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..0279b79 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,34 @@ +/**config api */ +import { ref } from "vue"; + +const env = ref(process.env.NODE_ENV || "development"); +// if (process.env.VUE_APP_TEST) { +// env = "test"; +// } + +const config = ref({ + development: { + API_URI: "https://localhost:7006/api/v1", + // API_URI: "https://wsh.frappet.com", + MEET_URI: "meet.frappet.com", + }, + test: { + API_URI: "http://localhost:5010/api/v1", + MEET_URI: "meet.frappet.com", + }, + production: { + // API_URI: "https://localhost:5010", + API_URI: `${window.location.protocol}//api-${window.location.host}/api/v1`, + MEET_URI: "meet.frappet.com", + }, +}); + +const API_URI = ref(config.value[env.value].API_URI); +const MEET_URI = ref(config.value[env.value].MEET_URI); + +export default { + env: env.value, + config: config.value, + API_URI: API_URI.value, + MEET_URI: MEET_URI.value, +}; diff --git a/src/api/manage/api.organization.ts b/src/api/manage/api.organization.ts new file mode 100644 index 0000000..5255dea --- /dev/null +++ b/src/api/manage/api.organization.ts @@ -0,0 +1,7 @@ +import env from '../index' +const dashbord = `${env.API_URI}/dashbord/` + +export default { + countDashbordSubHistory: (type: number) => `${dashbord}${type}`, + countDashbordHistory: `${dashbord}` +} diff --git a/src/app.config.ts b/src/app.config.ts new file mode 100644 index 0000000..6370ef3 --- /dev/null +++ b/src/app.config.ts @@ -0,0 +1,11 @@ +/**ใช้รวมไฟล์ย่อยๆ ของ api แต่ละไฟล์ */ + +import manageOrganization from './api/manage/api.organization' + +const API = { + ...manageOrganization +} + +export default { + API: API +} diff --git a/src/components/DialogFooter.vue b/src/components/DialogFooter.vue new file mode 100644 index 0000000..c2426f3 --- /dev/null +++ b/src/components/DialogFooter.vue @@ -0,0 +1,128 @@ + + diff --git a/src/components/DialogHeader.vue b/src/components/DialogHeader.vue new file mode 100644 index 0000000..95cdcdb --- /dev/null +++ b/src/components/DialogHeader.vue @@ -0,0 +1,32 @@ + + diff --git a/src/components/NotifyConfirm.vue b/src/components/NotifyConfirm.vue new file mode 100644 index 0000000..1796817 --- /dev/null +++ b/src/components/NotifyConfirm.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/components/NotifyError.vue b/src/components/NotifyError.vue new file mode 100644 index 0000000..bfef40f --- /dev/null +++ b/src/components/NotifyError.vue @@ -0,0 +1,92 @@ + + + diff --git a/src/components/Table.vue b/src/components/Table.vue new file mode 100644 index 0000000..30392db --- /dev/null +++ b/src/components/Table.vue @@ -0,0 +1,222 @@ + + + diff --git a/src/components/TableView.vue b/src/components/TableView.vue new file mode 100644 index 0000000..4f17b78 --- /dev/null +++ b/src/components/TableView.vue @@ -0,0 +1,481 @@ + + + diff --git a/src/components/top.vue b/src/components/top.vue new file mode 100644 index 0000000..4341451 --- /dev/null +++ b/src/components/top.vue @@ -0,0 +1,165 @@ + + + diff --git a/src/interface/main/index.ts b/src/interface/main/index.ts index f5bc0bc..3c39ec2 100644 --- a/src/interface/main/index.ts +++ b/src/interface/main/index.ts @@ -33,15 +33,8 @@ const menuList = readonly([ key: 2, icon: 'o_person', activeIcon: 'person', - label: 'ข้อมูลหลัก01', - path: 'meta01' - }, - { - key: 3, - icon: 'o_person', - activeIcon: 'person', - label: 'ข้อมูลหลัก02', - path: 'meta02' + label: 'รายการสอบทั้งหมด', + path: 'exam' } ]) @@ -70,7 +63,7 @@ const notiList = readonly([ { id: 1, sender: 'ท', - body: 'ขอแก้ไขข้อมูลทะเบียนประวัติ', + body: 'ขอแก้ไขข้อมูลรายการสอบทั้งหมด', timereceive: '13/12/2565' } ]) diff --git a/src/main.ts b/src/main.ts index eec266f..27e8ef4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,21 +1,23 @@ -import { createApp } from 'vue' -import { createPinia } from 'pinia' -import { Dialog, Notify, Quasar } from 'quasar' - +import { createApp, defineAsyncComponent } from 'vue' import App from './App.vue' import router from './router' +import { Dialog, Notify, Quasar } from 'quasar' import quasarUserOptions from './quasar-user-options' import 'quasar/src/css/index.sass' import th from 'quasar/lang/th' +import '@vuepic/vue-datepicker/dist/main.css' +import http from './plugins/http' +import { createPinia } from 'pinia' + // import './assets/main.css' const app = createApp(App) const pinia = createPinia() -app.use(pinia) app.use(router) +app.use(pinia) app.use( Quasar, @@ -30,8 +32,27 @@ app.use( } }, lang: th - }, - quasarUserOptions + } + // quasarUserOptions ) +app.component( + 'data-table', + defineAsyncComponent(() => import('./components/TableView.vue')) +) +app.component( + 'notifyError', + defineAsyncComponent(() => import('./components/NotifyError.vue')) +) +app.component( + 'datepicker', + defineAsyncComponent(() => import('@vuepic/vue-datepicker')) +) +app.component( + 'full-loader', + defineAsyncComponent(() => import('./plugins/FullLoader.vue')) +) + +app.config.globalProperties.$http = http + app.mount('#app') diff --git a/src/modules/01_exam/components/ExamCrad.vue b/src/modules/01_exam/components/ExamCrad.vue new file mode 100644 index 0000000..dde74f8 --- /dev/null +++ b/src/modules/01_exam/components/ExamCrad.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/modules/01_exam/components/ExamDetail.vue b/src/modules/01_exam/components/ExamDetail.vue new file mode 100644 index 0000000..9205d72 --- /dev/null +++ b/src/modules/01_exam/components/ExamDetail.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/modules/01_exam/components/ExamFinished.vue b/src/modules/01_exam/components/ExamFinished.vue new file mode 100644 index 0000000..1ab0a06 --- /dev/null +++ b/src/modules/01_exam/components/ExamFinished.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/modules/01_exam/components/ExamForm.vue b/src/modules/01_exam/components/ExamForm.vue new file mode 100644 index 0000000..1caf704 --- /dev/null +++ b/src/modules/01_exam/components/ExamForm.vue @@ -0,0 +1,194 @@ + + + + + + diff --git a/src/modules/01_exam/components/ExamPayment.vue b/src/modules/01_exam/components/ExamPayment.vue new file mode 100644 index 0000000..f9c6496 --- /dev/null +++ b/src/modules/01_exam/components/ExamPayment.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/src/modules/01_exam/components/Form/Career.vue b/src/modules/01_exam/components/Form/Career.vue new file mode 100644 index 0000000..8cb9506 --- /dev/null +++ b/src/modules/01_exam/components/Form/Career.vue @@ -0,0 +1,632 @@ + + + + diff --git a/src/modules/01_exam/components/Form/Document.vue b/src/modules/01_exam/components/Form/Document.vue new file mode 100644 index 0000000..dd15887 --- /dev/null +++ b/src/modules/01_exam/components/Form/Document.vue @@ -0,0 +1,134 @@ + + + diff --git a/src/modules/01_exam/components/Form/Education.vue b/src/modules/01_exam/components/Form/Education.vue new file mode 100644 index 0000000..e9de0e0 --- /dev/null +++ b/src/modules/01_exam/components/Form/Education.vue @@ -0,0 +1,661 @@ + + + + diff --git a/src/modules/01_exam/components/Form/Profile.vue b/src/modules/01_exam/components/Form/Profile.vue new file mode 100644 index 0000000..f29f33d --- /dev/null +++ b/src/modules/01_exam/components/Form/Profile.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/modules/01_exam/components/Form/Profile/Address.vue b/src/modules/01_exam/components/Form/Profile/Address.vue new file mode 100644 index 0000000..a2e284d --- /dev/null +++ b/src/modules/01_exam/components/Form/Profile/Address.vue @@ -0,0 +1,421 @@ + + + diff --git a/src/modules/01_exam/components/Form/Profile/Family.vue b/src/modules/01_exam/components/Form/Profile/Family.vue new file mode 100644 index 0000000..e804fb7 --- /dev/null +++ b/src/modules/01_exam/components/Form/Profile/Family.vue @@ -0,0 +1,361 @@ + + + diff --git a/src/modules/01_exam/components/Form/Profile/Information.vue b/src/modules/01_exam/components/Form/Profile/Information.vue new file mode 100644 index 0000000..9af96b7 --- /dev/null +++ b/src/modules/01_exam/components/Form/Profile/Information.vue @@ -0,0 +1,494 @@ + + + + + diff --git a/src/modules/01_exam/components/Form/Profile/Occupation.vue b/src/modules/01_exam/components/Form/Profile/Occupation.vue new file mode 100644 index 0000000..31b951b --- /dev/null +++ b/src/modules/01_exam/components/Form/Profile/Occupation.vue @@ -0,0 +1,297 @@ + + + diff --git a/src/modules/01_exam/interface/index/Main.ts b/src/modules/01_exam/interface/index/Main.ts new file mode 100644 index 0000000..0c17658 --- /dev/null +++ b/src/modules/01_exam/interface/index/Main.ts @@ -0,0 +1,283 @@ +interface Pagination { + rowsPerPage: number +} + +interface DataDateMonthObject { + month: number + year: number +} + +interface ChangeActive { + name: string + id: number +} + +//ข้อมูลส่วนตัว +interface Information { + cardid: string + prefix: string + prefixId: string + firstname: string + lastname: string + birthDate: Date + genderId: string + bloodId: string + nationality: string + ethnicity: string + religionId: string + tel: string + phone: string + email: string + provinceId: string + cardIdDate: Date + statusId: string + knowledge: string +} + +interface Family { + prefixC: string // couple + prefixIdC: string + firstnameC: string + lastnameC: string + occupationC: string + nationalityC: string + prefixM: string // male + prefixIdM: string + firstnameM: string + lastnameM: string + occupationM: string + nationalityM: string + prefixF: string // female + prefixIdF: string + firstnameF: string + lastnameF: string + occupationF: string + nationalityF: string + same: string +} + +interface Occupation { + status: string + company: string + department: string + email: string + tel: string + official: string | null + personnel: string | null + officialsOther: string | null + employee: string | null + other: string | null +} + +interface Address { + address: string + provinceId: string + districtId: string + subdistrictId: string + code: number + addressC: string + provinceIdC: string + districtIdC: string + subdistrictIdC: string + codeC: number + same: string +} + +interface DataOption { + id: string + name: string + zipCode?: string +} + +interface zipCodeOption { + id: string + name: string + zipCode: string +} + +interface ExamCard { + id: string + title: string + announcementDate: Date + registerRound: Number + registerDateStart: Date + registerDateEnd: Date + yearly: Date +} + +const defaultCard: ExamCard[] = [ + { + id: '1', + title: 'การสอบภาค ข. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 2, + announcementDate: new Date('2022-11-23'), + registerDateStart: new Date('2022-11-09'), + registerDateEnd: new Date('2022-11-10'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + }, + { + id: '2', + title: 'การสอบภาค ก. พิเศษ สำหรับผู้สอบผ่านของส่วนราชการแล้ว', + registerRound: 1, + announcementDate: new Date('2022-10-14'), + registerDateStart: new Date('2022-10-09'), + registerDateEnd: new Date('2022-11-30'), + yearly: new Date('2022-01-01') + } +] + +const defaultAddress: Address = { + address: 'บ้านเลขที่ 1/2 ซอย 56 ถนนตัดใหม่', + provinceId: 'กรุงเทพ', + districtId: 'หนองแขม', + subdistrictId: 'หนองค้างพลู', + code: 10160, + addressC: 'บ้านเลขที่ 1/2 ซอย 56 ถนนตัดใหม่', + provinceIdC: 'กรุงเทพ', + districtIdC: 'หนองแขม', + subdistrictIdC: 'หนองค้างพลู', + codeC: 10160, + same: '0' +} + +const defaultInformation: Information = { + cardid: '10238002345325', + prefix: 'นางสาว', + prefixId: 'นางสาว', + firstname: 'ณัฐกา', + lastname: 'ชมสิน', + birthDate: new Date('2002-01-01'), + genderId: 'หญิง', + bloodId: 'O', + nationality: 'ไทย', + ethnicity: 'ไทย', + religionId: 'พุทธ', + tel: '0914569982', + phone: '0914569982', + email: 'kittapath@frappet.com', + provinceId: 'กรุงเทพ', + cardIdDate: new Date('2000-01-10'), + statusId: 'โสด', + knowledge: 'excel, word, photoshop' +} + +const defaultFamily: Family = { + prefixC: 'นาย', + prefixIdC: 'นาย', + firstnameC: 'ธนาคาร', + lastnameC: 'กสิกร', + occupationC: 'ว่าง', + nationalityC: 'ไทย', + prefixM: 'นาย', + prefixIdM: 'นาย', + firstnameM: 'ธนายุทธ', + lastnameM: 'ชมสิน', + occupationM: 'ว่าง', + nationalityM: 'ไทย', + prefixF: 'นางสาว', + prefixIdF: 'นางสาว', + firstnameF: 'ณัฐกาล', + lastnameF: 'ชมสิน', + occupationF: 'ว่าง', + nationalityF: 'ไทย', + same: '1' +} + +const defaultOccupation: Occupation = { + status: 'official', + company: 'บริษัท ทรู คอร์ปอเรชั่น จำกัด (มหาชน)', + department: '-', + email: 'kittapath@frappet.com', + tel: '0846464646', + official: 'Direct Sales Staff', + personnel: null, + officialsOther: null, + employee: null, + other: null +} + +export { defaultInformation, defaultFamily, defaultAddress, defaultOccupation, defaultCard } +export type { + Pagination, + DataOption, + DataDateMonthObject, + ChangeActive, + Information, + Family, + Address, + zipCodeOption, + Occupation, + ExamCard +} diff --git a/src/modules/01_exam/interface/request/Career.ts b/src/modules/01_exam/interface/request/Career.ts new file mode 100644 index 0000000..0489f55 --- /dev/null +++ b/src/modules/01_exam/interface/request/Career.ts @@ -0,0 +1,29 @@ +interface DataProps { + row: RequestItemsObject + rowIndex: number +} + +//ข้อมูล +interface RequestItemsObject { + id: string + location: string + position: string + salary: number | null + duration: [Date, Date] + reason: string +} + +//columns +interface Columns { + [index: number]: { + name: String + align: String + label: String + sortable: Boolean + field: String + headerStyle: String + style: String + } +} + +export type { RequestItemsObject, Columns, DataProps } diff --git a/src/modules/01_exam/interface/request/Education.ts b/src/modules/01_exam/interface/request/Education.ts new file mode 100644 index 0000000..a275ec4 --- /dev/null +++ b/src/modules/01_exam/interface/request/Education.ts @@ -0,0 +1,30 @@ +interface DataProps { + row: RequestItemsObject + rowIndex: number +} + +//ข้อมูล +interface RequestItemsObject { + id: string + qualificationId: string + qualification: string + major: string + scores: number | null + name: string + duration: [Date, Date] +} + +//columns +interface Columns { + [index: number]: { + name: String + align: String + label: String + sortable: Boolean + field: String + headerStyle: String + style: String + } +} + +export type { RequestItemsObject, Columns, DataProps } diff --git a/src/modules/01_exam/interface/request/Main.ts b/src/modules/01_exam/interface/request/Main.ts new file mode 100644 index 0000000..dc6b137 --- /dev/null +++ b/src/modules/01_exam/interface/request/Main.ts @@ -0,0 +1,30 @@ +//ข้อมูล +interface RequestItemsObject { + id: number; + fullname: String; + avatar: String; + citizenId: String; + position: String; + line: String; + linePosition: String; + level: String; + positionFormalManage: String; + positionManage: String; + numberPosition: String; + government: String; +} + +//columns +interface Columns { + [index: number]: { + name: String; + align: String; + label: String; + sortable: Boolean; + field: String; + headerStyle: String; + style: String; + }; +} + +export type { RequestItemsObject, Columns }; diff --git a/src/modules/01_exam/interface/response/Career.ts b/src/modules/01_exam/interface/response/Career.ts new file mode 100644 index 0000000..f8b1f39 --- /dev/null +++ b/src/modules/01_exam/interface/response/Career.ts @@ -0,0 +1,11 @@ +//ข้อมูล +interface ResponseObject { + id: string + location: string + position: string + salary: number | null + duration: [Date, Date] + reason: string +} + +export type { ResponseObject } diff --git a/src/modules/01_exam/interface/response/Education.ts b/src/modules/01_exam/interface/response/Education.ts new file mode 100644 index 0000000..ed0fa20 --- /dev/null +++ b/src/modules/01_exam/interface/response/Education.ts @@ -0,0 +1,12 @@ +//ข้อมูล +interface ResponseObject { + id: string + qualificationId: string + qualification: string + major: string + scores: number | null + name: string + duration: [Date, Date] +} + +export type { ResponseObject } diff --git a/src/modules/01_exam/interface/response/Main.ts b/src/modules/01_exam/interface/response/Main.ts new file mode 100644 index 0000000..6771a73 --- /dev/null +++ b/src/modules/01_exam/interface/response/Main.ts @@ -0,0 +1,17 @@ +//ข้อมูล +interface ResponseObject { + id: number; + fullname: String; + avatar: String; + citizenId: String; + position: String; + line: String; + linePosition: String; + level: String; + positionFormalManage: String; + positionManage: String; + numberPosition: String; + government: String; +} + +export type { ResponseObject }; diff --git a/src/modules/01_exam/router.ts b/src/modules/01_exam/router.ts new file mode 100644 index 0000000..cba08b6 --- /dev/null +++ b/src/modules/01_exam/router.ts @@ -0,0 +1,23 @@ +const Main = () => import('@/modules/01_exam/views/ExamMain.vue') +const Detail = () => import('@/modules/01_exam/views/ExamDetail.vue') + +export default [ + // { + // path: '/exam', + // name: 'exam', + // component: Main, + // meta: { + // Auth: true + // // Key: [7] + // } + // }, + { + path: '/exam/:id', + name: 'examDetail', + component: Detail, + meta: { + Auth: true, + Key: [7] + } + } +] diff --git a/src/modules/01_exam/store.ts b/src/modules/01_exam/store.ts new file mode 100644 index 0000000..b1cc140 --- /dev/null +++ b/src/modules/01_exam/store.ts @@ -0,0 +1,32 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useExamDataStore = defineStore('exam', () => { + interface exam { + main: { columns: String[] } + education: { columns: String[] } + career: { columns: String[] } + } + + const examData = ref({ + main: { columns: [] }, + education: { columns: [] }, + career: { columns: [] } + }) + + const changeExamColumns = (system: String, val: String[]) => { + if (system == 'main') examData.value.main.columns = val + if (system == 'education') examData.value.education.columns = val + if (system == 'career') examData.value.career.columns = val + localStorage.setItem('exam', JSON.stringify(examData.value)) + } + + if (localStorage.getItem('exam') !== null) { + examData.value = JSON.parse(localStorage.getItem('exam') || '{}') + } + + return { + examData, + changeExamColumns + } +}) diff --git a/src/modules/01_exam/views/ExamDetail.vue b/src/modules/01_exam/views/ExamDetail.vue new file mode 100644 index 0000000..94a1584 --- /dev/null +++ b/src/modules/01_exam/views/ExamDetail.vue @@ -0,0 +1,152 @@ + + + + diff --git a/src/modules/01_exam/views/ExamMain.vue b/src/modules/01_exam/views/ExamMain.vue new file mode 100644 index 0000000..9daf7cf --- /dev/null +++ b/src/modules/01_exam/views/ExamMain.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/src/modules/01_meta/interface/index/main.ts b/src/modules/01_meta/interface/index/main.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/01_meta/interface/request/index.ts b/src/modules/01_meta/interface/request/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/01_meta/interface/response/index.ts b/src/modules/01_meta/interface/response/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/01_meta/router.ts b/src/modules/01_meta/router.ts deleted file mode 100644 index b9982e7..0000000 --- a/src/modules/01_meta/router.ts +++ /dev/null @@ -1,13 +0,0 @@ -const Meta = () => import('@/modules/01_meta/views/Meta01View.vue') - -export default [ - { - path: '/meta01', - name: 'meta01', - component: Meta, - meta: { - Auth: true - // Key: [7] - } - } -] diff --git a/src/modules/01_meta/store.ts b/src/modules/01_meta/store.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/01_meta/views/Meta01View.vue b/src/modules/01_meta/views/Meta01View.vue deleted file mode 100644 index 17ad0af..0000000 --- a/src/modules/01_meta/views/Meta01View.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/src/modules/02_meta/interface/index/main.ts b/src/modules/02_meta/interface/index/main.ts index e69de29..254d833 100644 --- a/src/modules/02_meta/interface/index/main.ts +++ b/src/modules/02_meta/interface/index/main.ts @@ -0,0 +1,10 @@ +interface Pagination { + rowsPerPage: number +} + +interface DataOption { + id: string + name: string +} + +export type { Pagination, DataOption } diff --git a/src/modules/02_meta/interface/request/Main.ts b/src/modules/02_meta/interface/request/Main.ts new file mode 100644 index 0000000..1d8f36d --- /dev/null +++ b/src/modules/02_meta/interface/request/Main.ts @@ -0,0 +1,28 @@ +interface DataProps { + row: RequestItemsObject + rowIndex: number +} + +//ข้อมูล +interface RequestItemsObject { + id: string + name: string + certiNumber: string + start: Date + end: Date +} + +//columns +interface Columns { + [index: number]: { + name: String + align: String + label: String + sortable: Boolean + field: String + headerStyle: String + style: String + } +} + +export type { RequestItemsObject, Columns, DataProps } diff --git a/src/modules/02_meta/interface/request/index.ts b/src/modules/02_meta/interface/request/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/02_meta/interface/response/Main.ts b/src/modules/02_meta/interface/response/Main.ts new file mode 100644 index 0000000..9a1f525 --- /dev/null +++ b/src/modules/02_meta/interface/response/Main.ts @@ -0,0 +1,11 @@ +//ข้อมูล +interface ResponseObject { + id: string + date: Date + status: string + level: string + refNo: string + refDate: Date +} + +export type { ResponseObject } diff --git a/src/modules/02_meta/interface/response/index.ts b/src/modules/02_meta/interface/response/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/02_meta/store.ts b/src/modules/02_meta/store.ts index e69de29..b0e868c 100644 --- a/src/modules/02_meta/store.ts +++ b/src/modules/02_meta/store.ts @@ -0,0 +1,10 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' + +export const useMetaStore = defineStore('meta', () => { + const meta = ref('') + + return { + meta + } +}) diff --git a/src/plugins/FullLoader.vue b/src/plugins/FullLoader.vue new file mode 100644 index 0000000..c517d7f --- /dev/null +++ b/src/plugins/FullLoader.vue @@ -0,0 +1,21 @@ + + + + diff --git a/src/plugins/axios.ts b/src/plugins/axios.ts new file mode 100644 index 0000000..79bba4e --- /dev/null +++ b/src/plugins/axios.ts @@ -0,0 +1,25 @@ +import axios from "axios" +import config from "process" +// import { dotnetPath } from "../path/axiosPath"; +// import { getToken } from "@baloise/vue-keycloak"; +import keycloak from "../plugins/keycloak" + +const axiosInstance = axios.create({ + withCredentials: false, +}) + +// axiosInstance.defaults.baseURL = dotnetPath; +axiosInstance.interceptors.request.use( + async (config) => { + const token = await keycloak.token + config.headers = { + Authorization: `Bearer ${token}`, + } + return config + }, + (error) => { + Promise.reject(error) + } +) + +export default axiosInstance diff --git a/src/plugins/http.ts b/src/plugins/http.ts new file mode 100644 index 0000000..494c9aa --- /dev/null +++ b/src/plugins/http.ts @@ -0,0 +1,45 @@ +import Axios, { type AxiosRequestConfig, type AxiosResponse } from "axios"; +import keycloak from "./keycloak"; + +const http = Axios.create({ + timeout: 1000000000, // เพิ่มค่า timeout + headers: { + "X-Requested-With": "XMLHttpRequest", + }, +}); + +http.interceptors.request.use( + async function (config: AxiosRequestConfig) { + await keycloak.updateToken(1); + config.headers = config.headers ?? {}; + const token = keycloak.token; + // const token = localStorage.getItem("access_token") + // const token = + // "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxU2VKV2dVRFVlNXZwNS13Q1ZHaG9lT2l4bDJTTkdKemthLU5ZN211NXZJIn0.eyJleHAiOjE2NzI0MTI1NDksImlhdCI6MTY3MjM3NjU0OSwiYXV0aF90aW1lIjoxNjcyMzc2NTQ5LCJqdGkiOiI1MTVhY2IwNC1jODQ3LTQzM2YtYjUxOC03ODUzMzJhY2ZjNWYiLCJpc3MiOiJodHRwczovL2tleWNsb2FrLmZyYXBwZXQuc3lub2xvZ3kubWUvYXV0aC9yZWFsbXMvYm1hLWVociIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJlZmM5YjRlMC1mZGU2LTQ1NDQtYmU1OS1lMTA0MjEwMjUzZjAiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJibWEtZWhyIiwibm9uY2UiOiI3NjMyMGI3ZS0xZTMxLTQ5ODYtYWIzOC1iOTUyYjFlODY3OGYiLCJzZXNzaW9uX3N0YXRlIjoiMDZlNTBkZjktNzAyNi00ZGIwLTkxMjgtMWY3Y2FiYTRkNDEyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwczovL2xvY2FsaG9zdDo3MDA2Il0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLWJtYS1laHIiLCJvZmZsaW5lX2FjY2VzcyIsImFkbWluIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwic2lkIjoiMDZlNTBkZjktNzAyNi00ZGIwLTkxMjgtMWY3Y2FiYTRkNDEyIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInJvbGUiOlsiZGVmYXVsdC1yb2xlcy1ibWEtZWhyIiwib2ZmbGluZV9hY2Nlc3MiLCJhZG1pbiIsInVtYV9hdXRob3JpemF0aW9uIl0sIm5hbWUiOiJTeXN0ZW0gQWRtaW5pc3RyYXRvciIsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwiZ2l2ZW5fbmFtZSI6IlN5c3RlbSIsImZhbWlseV9uYW1lIjoiQWRtaW5pc3RyYXRvciIsImVtYWlsIjoiYWRtaW5AbG9jYWxob3N0In0.xmfJ3pzI-jLYsaiFXyjTW7gfAEpvUmMVsp9BsB1CfRCVOKiGBbuZhnQY8W-1SWVAx1NjJ55L-zMHPK6hk1dRPLbEse3DlIBZw04W9j8m-Wz3eqdHf_UCjmrXb8qAwkeq0Iaxq9mVfJJeQWeKhFBi-Ff8ek4hCXTYDICXS8ny_BaC5WkyrefHQ2xBqQjwRyoxsg4IoVMjXYNb8L9A-4BNlRfs928SqgFYCRlF5h6zw_rC0XoLrGTmqeacBdpey-r3j2g_lTqWy8mQg2T9s65IDqW3kFPOsr0SVO88sjlFbN9Et0L57RmiqORk_RwzbWg-_Yb6dOuolXsnjBOhOoTzkA"; + if (token) config.headers.Authorization = `Bearer ${token}`; + return config; + }, + function (error: any) { + return Promise.reject(error); + } +); + +http.interceptors.response.use( + function (response: AxiosResponse) { + 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; diff --git a/src/plugins/keycloak.ts b/src/plugins/keycloak.ts new file mode 100644 index 0000000..7aeee92 --- /dev/null +++ b/src/plugins/keycloak.ts @@ -0,0 +1,23 @@ +/** + * front connect to keycloak + */ +import Keycloak from 'keycloak-js' +// import config from "../app.config"; +// import http from "../shared/http"; +// import router from "../router"; + +const initOptions = { + // url: "https://keycloak.frappet.synology.me/auth/", + // realm: "bma-ehr", + // clientId: "bma-ehr-vue3", + url: 'https://identity.frappet.com/', + realm: 'exam_test', + clientId: 'exam_vue3' +} //option keycloak ที่จะ connect + +const keycloak = Keycloak(initOptions) + +keycloak.onAuthSuccess = () => {} //เพิ่มlogin สำเร็จจะมาทำฟังก์ชันนี้ + +await keycloak.init({ checkLoginIframe: false }) //ทำการ connect keycloak +export default keycloak diff --git a/src/router/index.ts b/src/router/index.ts index c9a28d6..24f600a 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,31 +1,34 @@ import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' -import Meta01 from '@/modules/01_meta/router' +import Exam from '@/modules/01_exam/router' import Meta02 from '@/modules/02_meta/router' +import keycloak from '@/plugins/keycloak' + const MainLayout = () => import('@/views/MainLayout.vue') +const Error404NotFound = () => import('@/views/Error404NotFound.vue') const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { - path: '/', - name: 'home', + path: '/exam/:id', + // name: 'home', component: MainLayout, children: [ - { - path: '/', - name: 'dashboard', - component: HomeView, - meta: { - Auth: true, - Key: [7] - } - }, - ...Meta01, - ...Meta02 + // { + // path: '/', + // name: 'dashboard', + // component: HomeView, + // meta: { + // Auth: true, + // Key: [7] + // } + // }, + ...Exam + // ...Meta02 ] - } + }, /** * 404 Not Found * ref: https://router.vuejs.org/guide/essentials/dynamic-matching.html#catch-all-404-not-found-route @@ -35,6 +38,11 @@ const router = createRouter({ // path: "/:pathMatch(.*)*", // component: Error404NotFound, // }, + { + // path: "/:catchAll(.*)*", // TODO: ใช้ pathMatch แทนตามในเอกสารแนะนำ คงไว้เผื่อจำเป็น + path: '/:pathMatch(.*)*', + component: Error404NotFound + } ] }) @@ -56,5 +64,21 @@ const router = createRouter({ // } // ] // }) +router.beforeEach((to, from, next) => { + // if (to.meta.Auth) { + // if (!keycloak.authenticated) { + // keycloak.login({ + // redirectUri: `${window.location.protocol}//${window.location.host}${to.path}`, + // locale: 'th' + // }) + // } else { + // // keycloak.updateToken(60); + // next() + // } + // } else { + // next() + // } + next() +}) export default router diff --git a/src/stores/mixin.ts b/src/stores/mixin.ts new file mode 100644 index 0000000..ee647da --- /dev/null +++ b/src/stores/mixin.ts @@ -0,0 +1,348 @@ +import { ref, computed } from 'vue' +import { defineStore } from 'pinia' +import moment from 'moment' +import type { DataDateMonthObject } from '@/modules/01_exam/interface/index/Main' + +export const useCounterMixin = defineStore('mixin', () => { + /** + * ฟังก์ชันกลาง + */ + + const calAge = (srcDate: Date, birthCal: Date = new Date(), eng: boolean = false) => { + const toDay = birthCal + const birth = new Date(srcDate) + + const yearNow = toDay.getFullYear() + const monthNow = toDay.getMonth() + const dateNow = toDay.getDate() + + const yearDob = birth.getFullYear() + const monthDob = birth.getMonth() + const dateDob = birth.getDate() + + const lastYear = 12 + const subtractDate: Object = moment().subtract(1, 'months').endOf('month') + + const lastMonths = new Date(subtractDate.toString()).getDate() + + let yearAge = yearNow - yearDob + let monthAge = 0 + let dateAge = 0 + + if (monthNow >= monthDob) { + monthAge = monthNow - monthDob + } else { + yearAge-- + monthAge = lastYear + monthNow - monthDob + } + + if (dateNow >= dateDob) { + dateAge = dateNow - dateDob + } else { + monthAge-- + dateAge = lastMonths + dateNow - dateDob + + if (monthAge < 0) { + monthAge = 11 + yearAge-- + } + } + + const age = { + years: yearAge, + months: monthAge, + days: dateAge + } + + const year = eng ? 'years' : 'ปี' + const month = eng ? 'months' : 'เดือน' + const day = eng ? 'days' : 'วัน' + return `${yearAge} ${year} ${monthAge} ${month} ${dateAge} ${day}` + } + + function date2Thai(srcDate: Date, isFullMonth: boolean = false, isTime: boolean = false) { + 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 + } + + function monthYear2Thai(month: number, year: number, isFullMonth = false) { + const date = new Date(`${year}-${month + 1}-1`) + 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()] + } + return dstMonth + ' ' + dstYear + } + + function dateToISO(date: Date) { + return ( + date.getFullYear() + + '-' + + appendLeadingZeroes(date.getMonth() + 1) + + '-' + + appendLeadingZeroes(date.getDate()) + ) + } + + function appendLeadingZeroes(n: Number) { + if (n <= 9) return '0' + n + return n + } + + function textToPhone(n: string) { + const p = n.substr(0, 3) + '-' + n.substr(3, 3) + '-' + n.substr(6, 4) + return p + } + + function textToFax(n: string) { + const p = n.substr(0, 2) + '-' + n.substr(2, 3) + '-' + n.substr(5, 4) + return p + } + + const success = (q: any, val: string) => { + // useQuasar ไม่สามารถใช้นอกไฟล์ .vue + if (val !== '') { + return q.notify({ + message: val, + color: 'primary', + icon: 'mdi-information', + position: 'bottom-right', + multiLine: true, + timeout: 1000, + badgeColor: 'positive', + classes: 'my-notif-class' + }) + } + } + + function notify(q: any, val: string) { + if (val !== '') { + q.notify({ + color: 'teal-10', + message: val, + icon: 'mdi-information', + position: 'bottom-right', + multiLine: true, + timeout: 7000, + actions: [{ label: 'ปิด', color: 'white', handler: () => {} }] + }) + } + } + function notifyError(q: any, val: string) { + if (val !== '') { + q.notify({ + color: 'negative', + message: val, + icon: 'mdi-alert-circle', + position: 'top', + multiLine: true, + timeout: 12000, + actions: [{ label: 'ปิด', color: 'white', handler: () => {} }] + }) + } + } + + const dateText = (val: Date) => { + if (val != null) { + return date2Thai(val) + } else { + return '-' + } + } + + const weekThai = (val: Number) => { + switch (val) { + case 0: + return 'วันอาทิตย์' + case 1: + return 'วันจันทร์' + case 2: + return 'วันอังคาร' + case 3: + return 'วันพุธ' + case 4: + return 'วันพฤหัสบดี' + case 5: + return 'วันศุกร์' + case 6: + return 'วันเสาร์' + default: + return '-' + } + } + + const genColor15 = (val: number) => { + val = val % 15 + switch (val) { + case 1: + return 'pink' + case 2: + return 'purple' + case 3: + return 'deep-purple' + case 4: + return 'indigo' + case 5: + return 'blue' + case 6: + return 'light-blue' + case 7: + return 'cyan' + case 8: + return 'teal' + case 9: + return 'green' + case 10: + return 'light-green' + case 11: + return 'amber' + case 12: + return 'orange' + case 13: + return 'deep-orange' + case 14: + return 'brown' + case 0: + return 'blue-grey' + default: + return '' + } + } + + /** + * แปลงช่วงวันที่ถ้า2ค่าเป็นวันเดียวกันจะโชววันเดียวแต่ถ้าไม่เท่ากันจะแสดงเป็นช่วง + * @param val ช่วงวันที่ + */ + const dateThaiRange = (val: [Date, Date]) => { + if (val === null) { + return '' + } else if (date2Thai(val[0]) === date2Thai(val[1])) { + return `${date2Thai(val[0])}` + } else { + return `${date2Thai(val[0])} - ${date2Thai(val[1])}` + } + } + + /** + * แปลงช่วงเดือนถ้า2ค่าเป็นวันเดียวกันจะโชววันเดียวแต่ถ้าไม่เท่ากันจะแสดงเป็นช่วง + * @param val ช่วงเดือน + */ + const monthThaiRange = (val: DataDateMonthObject[]) => { + if (val === null || val[0] === null || val[1] === null) { + return '' + } else if ( + monthYear2Thai(val[0].month, val[0].year) === monthYear2Thai(val[1].month, val[1].year) + ) { + return `${monthYear2Thai(val[0].month, val[0].year)}` + } else { + return `${monthYear2Thai(val[0].month, val[0].year)} - ${monthYear2Thai( + val[1].month, + val[1].year + )}` + } + } + + return { + calAge, + date2Thai, + dateToISO, + notify, + notifyError, + dateText, + monthYear2Thai, + success, + weekThai, + genColor15, + textToPhone, + textToFax, + dateThaiRange, + monthThaiRange + } +}) diff --git a/src/views/Error404NotFound.vue b/src/views/Error404NotFound.vue new file mode 100644 index 0000000..e06a8c1 --- /dev/null +++ b/src/views/Error404NotFound.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/views/MainLayout.vue b/src/views/MainLayout.vue index 2a96e05..7a5dad9 100644 --- a/src/views/MainLayout.vue +++ b/src/views/MainLayout.vue @@ -1,11 +1,14 @@