Browse Source

More improvements, doctor page is mostly done.

production
Yiğit Çolakoğlu 4 years ago
parent
commit
9e90d4c912
24 changed files with 418 additions and 1236 deletions
  1. +37
    -81
      package-lock.json
  2. +11
    -0
      portal.iml
  3. +39
    -18
      src/App.vue
  4. +10
    -10
      src/components/AppointmentModal.vue
  5. +2
    -8
      src/components/DatetimePicker.vue
  6. +110
    -0
      src/components/DisabledCard.vue
  7. +27
    -0
      src/components/LoadingSpinner.vue
  8. +5
    -5
      src/components/LoginForm.vue
  9. +4
    -2
      src/components/Pagination.vue
  10. +4
    -4
      src/components/PatientCard.vue
  11. +76
    -0
      src/components/PatientModal.vue
  12. +3
    -5
      src/components/PatientSearchField.vue
  13. +1
    -1
      src/components/Typeahead.vue
  14. +0
    -74
      src/components/UserSearchField.vue
  15. +5
    -5
      src/router/index.js
  16. +1
    -1
      src/views/Calendar.vue
  17. +70
    -0
      src/views/Disabled.vue
  18. +2
    -1
      src/views/Login.vue
  19. +11
    -4
      src/views/Patients.vue
  20. +0
    -1
      src/vue-tailwind-datetime-picker/css/tailwind.css
  21. +0
    -943
      src/vue-tailwind-datetime-picker/vue-tailwind-datetime-picker.vue
  22. +0
    -28
      src/vue-tailwind-datetime-picker/wrapper.js
  23. +0
    -5
      src/vue-tailwindcss-typeahead/css/tailwind.css
  24. +0
    -40
      src/vue-tailwindcss-typeahead/entry.js

+ 37
- 81
package-lock.json View File

@ -2466,6 +2466,43 @@
"integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=",
"dev": true
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.2.tgz",
"integrity": "sha512-8QTxh+Fd+HB6fiL52iEVLKqE9N1JSlMXLR92Ijm6g8PZrwIxckgpqjPDWRP5TWxdiPaHR+alUWsnu1ShQOwt+Q==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
}
}
},
"webpack-bundle-analyzer": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz",
@ -14728,87 +14765,6 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.1.2",
"resolved": "https://registry.npm.taobao.org/vue-loader/download/vue-loader-16.1.2.tgz?cache=0&sync_timestamp=1608188078235&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fvue-loader%2Fdownload%2Fvue-loader-16.1.2.tgz",
"integrity": "sha1-XAO2xQ0qX5g8fOuhXFDXjKKymPQ=",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792436886&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz",
"integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1591687018980&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz",
"integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz",
"integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz",
"integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz",
"integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-2.0.0.tgz",
"integrity": "sha1-5MrOW4FtQloWa18JfhDNErNgZLA=",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1608035619713&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz",
"integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-popperjs": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/vue-popperjs/-/vue-popperjs-2.3.0.tgz",


+ 11
- 0
portal.iml View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.idea" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

+ 39
- 18
src/App.vue View File

@ -1,6 +1,6 @@
<template>
<div class="md:flex flex-col md:flex-row md:min-h-screen w-full">
<div v-if="loggedin && !hideSidebar" class="border-r-2 flex flex-col w-full md:w-56 mr-2 text-gray-700 bg-gray-100 dark-mode:text-gray-200 dark-mode:bg-gray-800 flex-shrink-0" x-data="{ collapsed : false }">
<div class="md:flex flex-col md:flex-row md:min-h-screen w-full h-full">
<div v-if="loggedin && !hideSidebar && display" class="border-r-2 flex flex-col w-full md:w-56 mr-2 text-gray-700 bg-gray-100 dark-mode:text-gray-200 dark-mode:bg-gray-800 flex-shrink-0" x-data="{ collapsed : false }">
<div class="flex-shrink-0 px-8 py-4 flex flex-row items-center justify-between">
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">Metis Portal</a>
<button class="rounded-lg md:hidden focus:outline-none focus:shadow-outline" @click="toggleNavbar">
@ -10,26 +10,35 @@
</button>
</div>
<nav :class="{'block': collapsed, 'hidden': !collapsed}" class="flex-grow md:block px-4 pb-4 md:pb-0 md:overflow-y-auto">
<router-link to="/calendar" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'calendar', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='calendar'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('calendar')">Calendar</router-link>
<router-link to="/patients" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'patients', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='patients'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('patients')">Patients</router-link>
<router-link to="/users" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'users', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='users'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('users')">Users</router-link>
<router-link to="/disabled" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'disabled', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='disabled'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('disabled')">Disabled</router-link>
<router-link to="/profile" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'profile', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='profile'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('profile')">Profile</router-link>
<router-link to="/settings" :class="{'bg-transparent dark-mode:bg-transparent': selected != 'settings', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='settings'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('settings')">Settings</router-link>
<a :class="{'bg-transparent dark-mode:bg-transparent': selected != 'logout', 'bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600': selected=='logout'}" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click="selectPage('logout')">Log Out</a>
<router-link to="/calendar" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('calendar')">Calendar</router-link>
<router-link v-if="role === 'DOCTOR' || role === 'ADMIN'" to="/patients" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('patients')">Patients</router-link>
<router-link v-if="role === 'ADMIN'" to="/users" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('users')">Users</router-link>
<router-link v-if="role === 'DOCTOR' || role === 'ADMIN'" to="/disabled" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('disabled')">Disabled</router-link>
<router-link to="/profile" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('profile')">Profile</router-link>
<router-link v-if="role === 'ADMIN'" to="/settings" active-class="bg-gray-200 dark-mode:text-white dark-mode:bg-gray-600" class="bg-transparent dark-mode:bg-transparent block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline" @click.native="selectPage('settings')">Settings</router-link>
<a @click="logout" class="block px-4 py-2 mt-2 text-sm font-semibold text-gray-900 rounded-lg dark-mode:hover:bg-gray-600 dark-mode:hover:text-white dark-mode:text-gray-200 hover:text-gray-900 focus:text-gray-900 hover:bg-gray-200 focus:outline-none focus:shadow-outline">Log Out</a>
</nav>
</div>
<router-view :locale="locale" class="flex-1 w-5/6"></router-view>
<router-view v-if="loggedin && display" :locale="locale" :loggedin="loggedin" class="flex-1 w-5/6"></router-view>
<Login v-if="!loggedin && display" />
<div v-if="!display" class="h-screen w-full flex items-center">
<LoadingSpinner class="mx-auto"/>
</div>
</div>
</template>
<script>
import Vue from 'vue'
import { StorageManagement } from "@sebgroup/frontend-tools";
import Login from "@/views/Login"
import router from './router'
import axios from 'axios'
import LoadingSpinner from "@/components/LoadingSpinner";
export default Vue.extend({
export default Vue.extend({
components : {
LoadingSpinner,
Login
},
data: function() {
return {
collapsed: false,
@ -38,7 +47,8 @@ export default Vue.extend({
hideSidebar: false,
loggedin: false,
token: "",
locale: ""
locale: "",
display: false
}
},
methods: {
@ -50,18 +60,28 @@ export default Vue.extend({
},
setLoginStatus: function(state){
this.loggedin = state;
if(state)
router.push("/calendar")
if(state) {
router.push("/calendar")
axios.get('http://localhost:9090/profile', {
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('JWT')}`
}
}).then((result) => this.locale = result.data.user.locale)
}
else
router.push("/login")
},
logout: function(){
this.loggedin = false;
window.localStorage.removeItem('JWT')
}
},
async created(){
const localStorage = new StorageManagement("LOCAL");
const localStorage = window.localStorage;
if(localStorage.getItem("JWT") == null){
this.loggedin = false;
this.display = true;
router.push("/login");
return;
}
@ -82,7 +102,8 @@ export default Vue.extend({
console.log(err);
this.loggedin = false;
}
this.token = localStorage.getItem("JWT");
this.token = localStorage.getItem("JWT");
this.display = true;
}
})
</script>

+ 10
- 10
src/components/AppointmentModal.vue View File

@ -15,33 +15,33 @@
<div class="flex items-center space-x-4">
<div class="flex flex-col">
<label class="leading-loose">Start</label>
<VueTailwindDatetimePicker @change="(v) => start = v" class="focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="start">
<DatetimePicker @change="(v) => start = v" class="focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="start">
<input v-model="start" type="text" class="pr-4 pl-10 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="25/02/2020">
<div class="absolute left-3 top-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
</VueTailwindDatetimePicker>
</DatetimePicker>
</div>
<div class="flex flex-col">
<label class="leading-loose">End</label>
<VueTailwindDatetimePicker @change="(v) => end = v" class="relative focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="end">
<DatetimePicker @change="(v) => end = v" class="relative focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="end">
<input v-model="end" type="text" class="pr-4 pl-10 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="25/02/2020">
<div class="absolute left-3 top-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
</VueTailwindDatetimePicker>
</DatetimePicker>
</div>
</div>
<div class="flex flex-col">
<label class="leading-loose">Patient</label>
<vue-tailwindcss-typeahead
<Typeahead
@selected="selectedPatient"
@searchFieldUpdated="updatePatientSuggestions"
:lists="patientSuggestions"
:clearInputWhenClicked="false"
:inputClass="['px-4', 'py-2', 'border', 'focus:ring-gray-500', 'focus:border-gray-900', 'w-full', 'sm:text-sm', 'border-gray-300', 'rounded-md', 'focus:outline-none', 'text-gray-600']"
placeholder="Patient Name">
</vue-tailwindcss-typeahead>
</Typeahead>
</div>
<div class="flex flex-col">
<label class="leading-loose">Event Description</label>
@ -67,16 +67,16 @@
</template>
<script>
import VueTailwindDatetimePicker from '@/components/vue-tailwind-datetime-picker'
import VueTailwindcssTypeahead from '@/vue-tailwindcss-typeahead/vue-tailwindcss-typeahead';
import DatetimePicker from '@/components/DatetimePicker'
import Typeahead from '@/components/Typeahead';
import dayjs from 'dayjs'
import axios from 'axios';
//import axios from 'axios';
export default{
components: {
VueTailwindDatetimePicker,
VueTailwindcssTypeahead
DatetimePicker,
Typeahead
},
// Data
data: function(){


src/components/vue-tailwind-datetime-picker.vue → src/components/DatetimePicker.vue View File


+ 110
- 0
src/components/DisabledCard.vue View File

@ -0,0 +1,110 @@
<template>
<div class="font-sans items-center flex flex-col md:flex-row p-3">
<div class="mr-5">
<p>Name:</p>
<input v-model="disabled.name" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="Patient Name" type="text">
</div>
<div class="mr-5">
<p>Start:</p>
<DatetimePicker @change="(v) => disabled.start = startFormat(v)" class="focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="start">
<input v-model="start" type="text" class="pr-4 pl-10 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="25/02/2020">
<div class="absolute left-3 top-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
</DatetimePicker>
</div>
<div class="mr-5">
<p>End:</p>
<DatetimePicker @change="(v) => disabled.duration = calcDuration(v)" class="focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="end">
<input v-model="end" type="text" class="pr-4 pl-10 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="25/02/2020">
<div class="absolute left-3 top-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
</DatetimePicker>
</div>
<div class="">
<p>Next:</p>
<DatetimePicker @change="(v) => disabled.repetition = calcInterval(v)" class="focus-within:text-gray-600 text-gray-400" :init="false" :startFromMonday="false" :autoClose="false" :startDate="next">
<input v-model="next" type="text" class="pr-4 pl-10 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="25/02/2020">
<div class="absolute left-3 top-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
</div>
</DatetimePicker>
</div>
<div class="h-1/4 w-full md:w-1/12 flex flex-row ml-auto content-end">
<button v-if="edited" @click="save" class="fill-current mt-2 md:mt-0 p-1 w-1/2 md:w-5/12 ml-2 flex bg-green-500 justify-center items-center text-white rounded-md focus:outline-none">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-6">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</button>
<button @click="$emit('delete', {obj: disabled, index: index})" :class="{'w-1/2': edited, 'w-full': !edited}" v-if="!nodelete" class="fill-current mt-2 md:mt-0 p-1 md:w-5/12 ml-2 flex bg-red-500 justify-center items-center text-white rounded-md focus:outline-none"><svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" /></svg></button>
</div>
</div>
</template>
<script>
import DatetimePicker from "@/components/DatetimePicker";
import dayjs from "dayjs";
export default {
name: "DisabledCard",
components: {DatetimePicker},
props: {
rule: Object,
index: Number,
nodelete: Boolean
},
data: function(){
return {
disabled: Object.assign({}, this.rule)
}
},
computed: {
end: function(){
if(this.disabled.start === '') {
console.log("EMPTY")
return dayjs().format('YYYY-MM-DDTHH:mm');
}
return dayjs(this.disabled.start).add(this.disabled.duration, 'ms').format('YYYY-MM-DDTHH:mm');
},
next: function(){
if(this.disabled.start === '') {
console.log("EMPTY")
return dayjs().format('YYYY-MM-DDTHH:mm');
}
return dayjs(this.disabled.start).add(this.disabled.duration + this.disabled.repetition, 'ms').format('YYYY-MM-DDTHH:mm');
},
start: function(){
if(this.disabled.start === '') {
console.log("EMPTY")
return dayjs().format('YYYY-MM-DDTHH:mm');
}
return dayjs(this.disabled.start).format('YYYY-MM-DDTHH:mm');
},
edited: function(){
let same = true
same = same && this.disabled.name === this.rule.name
same = same && this.disabled.duration === this.rule.duration
same = same && this.disabled.repetition === this.rule.repetition
console.log(this.disabled)
console.log(this.rule)
return !same
}
},
methods: {
save: function(){
this.rule = this.disabled
this.$emit('save', {'obj': this.disabled, 'index':this.index})
},
calcDuration: function(dateStr){
return dayjs(dateStr).diff(this.start, 'ms');
},
calcInterval: function(dateStr){
return dayjs(dateStr).diff(this.end, 'ms');
},
startFormat: function(dateStr){
return dayjs(dateStr).format('YYYY-MM-DDTHH:mmZ');
}
}
}
</script>

+ 27
- 0
src/components/LoadingSpinner.vue View File

@ -0,0 +1,27 @@
<template>
<div class="loader ease-linear rounded-full border-8 border-t-8 border-gray-200 h-32 w-32 md:h-64 md:w-64"></div>
</template>
<script>
export default {
name: "LoadingSpinner"
}
</script>
<style scoped>
.loader {
border-top-color: #3498db;
-webkit-animation: spinner 1.5s linear infinite;
animation: spinner 1.5s linear infinite;
}
@-webkit-keyframes spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>

+ 5
- 5
src/components/LoginForm.vue View File

@ -1,5 +1,5 @@
<template>
<div class="font-sans">
<div class="font-sans container max-w-full mx-auto py-24">
<div class="max-w-lg mx-auto px-6">
<div class="relative flex flex-wrap">
<div class="w-full relative">
@ -26,7 +26,7 @@
<div class="py-2">
<span class="px-1 text-sm text-gray-600">Password</span>
<div class="relative">
<input v-model="password" placeholder="" :type="!showPassword ? 'password' : 'text'" class="text-md block px-3 py-2 rounded-lg w-full
<input v-on:keyup.enter="apiLogin" v-model="password" placeholder="" :type="!showPassword ? 'password' : 'text'" class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md
focus:placeholder-gray-500
focus:bg-white
@ -49,7 +49,7 @@
href="#"
class="cursor-pointer tracking-tighter text-black border-b-2 border-gray-200 hover:border-gray-400"><span>Forgot
Password?</span></a></label></div>
<button @click="apiLogin" class="mt-3 text-lg font-semibold
<button @click="apiLogin" class="mt-3 text-lg font-semibold
bg-gray-800 w-full text-white rounded-lg
px-6 py-3 block shadow-xl hover:text-white hover:bg-black">
Login
@ -88,7 +88,7 @@ export default {
if(!isEmail(this.email)){
this.emailValid = false
return
}
}
this.emailValid = true
this.loginError = false
axios.post('http://localhost:9090/login', {
@ -112,7 +112,7 @@ export default {
}
this.loginError = true
});
}
}
}
}
</script>


+ 4
- 2
src/components/Pagination.vue View File

@ -62,10 +62,12 @@
if(this.pages <= 3){
return [...Array(this.pages).keys()].map((x) => { return this.pages + x })
}
if(this.pages - this.currentPage < 3){
else if(this.pages - this.currentPage < 3){
return [...Array(3).keys()].map((x) => { return this.pages - (2 - x) })
}else if(this.currentPage < 3){
return [1, 2, 3]
}
return [...Array(3).keys()].map((x) => { return this.currentPage + x })
return [...Array(3).keys()].map((x) => { return this.currentPage + x - 1})
}
},
methods: {


+ 4
- 4
src/components/PatientCard.vue View File

@ -1,14 +1,14 @@
<template>
<div class="font-sans items-center flex flex-col md:flex-row hover:bg-gray-100">
<div @click="$emit('showModal', patient)" class="font-sans items-center flex flex-col md:flex-row hover:bg-gray-100">
<div class="">
<h5>{{ patient.name }}</h5>
<p> {{ patient.phone }}</p>
<small class="text-gray-600">{{ patient.email }}</small>
</div>
<div class="h-1/4 w-full md:w-5/12 flex flex-row ml-auto content-end">
<button @click="covidCheck" :class="{'text-white bg-green-500' : patient.safe, 'text-black bg-yellow-500' : !patient.safe}" class="ml-full fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-auto flex justify-center items-center rounded-md focus:outline-none"><svg class="h-6" id="Layer_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m480 224c-11.82 0-22.16 6.44-27.7 16h-29.06c-3.26-34.338-16.889-65.697-37.727-90.886l20.612-20.612c10.603 2.854 22.516.104 30.894-8.261 12.48-12.48 12.48-32.78 0-45.26s-32.78-12.48-45.26 0c-8.349 8.359-11.109 20.226-8.261 30.894l-20.612 20.612c-25.189-20.838-56.548-34.467-90.886-37.727v-29.06c9.56-5.54 16-15.88 16-27.7 0-17.65-14.35-32-32-32s-32 14.35-32 32c0 11.82 6.44 22.16 16 27.7v29.06c-34.338 3.26-65.697 16.889-90.886 37.727l-20.612-20.612c2.848-10.669.087-22.536-8.261-30.894-12.48-12.48-32.78-12.48-45.26 0s-12.48 32.78 0 45.26c8.374 8.36 20.286 11.117 30.894 8.261l20.612 20.612c-20.838 25.189-34.467 56.548-37.727 90.886h-29.06c-5.54-9.56-15.88-16-27.7-16-17.65 0-32 14.35-32 32s14.35 32 32 32c11.82 0 22.16-6.44 27.7-16h29.06c3.26 34.338 16.889 65.697 37.727 90.886l-20.612 20.612c-10.668-2.848-22.536-.087-30.894 8.261-12.48 12.48-12.48 32.78 0 45.26 12.503 12.503 32.803 12.457 45.26 0 8.349-8.359 11.109-20.226 8.261-30.894l20.612-20.612c25.189 20.838 56.548 34.467 90.886 37.727v29.06c-9.56 5.54-16 15.88-16 27.7 0 17.65 14.35 32 32 32s32-14.35 32-32c0-11.82-6.44-22.16-16-27.7v-29.06c34.338-3.26 65.697-16.889 90.886-37.727l20.612 20.612c-2.848 10.668-.087 22.536 8.261 30.894 12.457 12.457 32.757 12.503 45.26 0 12.48-12.48 12.48-32.78 0-45.26-8.359-8.349-20.226-11.109-30.894-8.261l-20.612-20.612c20.838-25.189 34.467-56.548 37.727-90.886h29.06c5.54 9.56 15.88 16 27.7 16 17.65 0 32-14.35 32-32s-14.35-32-32-32zm-277 24c-15.44 0-28-12.56-28-28s12.56-28 28-28 28 12.56 28 28-12.56 28-28 28zm69 96c-17.65 0-32-14.35-32-32s14.35-32 32-32 32 14.35 32 32-14.35 32-32 32zm56-112c-13.23 0-24-10.77-24-24s10.77-24 24-24 24 10.77 24 24-10.77 24-24 24z"/></svg></button>
<button @click="changePassword" class="fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-2 flex bg-blue-500 justify-center items-center text-white rounded-md focus:outline-none"><svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z" clip-rule="evenodd" /></svg></button>
<button @click="deletePatient" class="fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-2 flex bg-red-500 justify-center items-center text-white rounded-md focus:outline-none"><svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" /></svg></button>
<button @click.stop="covidCheck" :class="{'text-white bg-green-500' : patient.safe, 'text-black bg-yellow-500' : !patient.safe}" class="ml-full fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-auto flex justify-center items-center rounded-md focus:outline-none"><svg class="h-6" id="Layer_1" enable-background="new 0 0 512 512" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m480 224c-11.82 0-22.16 6.44-27.7 16h-29.06c-3.26-34.338-16.889-65.697-37.727-90.886l20.612-20.612c10.603 2.854 22.516.104 30.894-8.261 12.48-12.48 12.48-32.78 0-45.26s-32.78-12.48-45.26 0c-8.349 8.359-11.109 20.226-8.261 30.894l-20.612 20.612c-25.189-20.838-56.548-34.467-90.886-37.727v-29.06c9.56-5.54 16-15.88 16-27.7 0-17.65-14.35-32-32-32s-32 14.35-32 32c0 11.82 6.44 22.16 16 27.7v29.06c-34.338 3.26-65.697 16.889-90.886 37.727l-20.612-20.612c2.848-10.669.087-22.536-8.261-30.894-12.48-12.48-32.78-12.48-45.26 0s-12.48 32.78 0 45.26c8.374 8.36 20.286 11.117 30.894 8.261l20.612 20.612c-20.838 25.189-34.467 56.548-37.727 90.886h-29.06c-5.54-9.56-15.88-16-27.7-16-17.65 0-32 14.35-32 32s14.35 32 32 32c11.82 0 22.16-6.44 27.7-16h29.06c3.26 34.338 16.889 65.697 37.727 90.886l-20.612 20.612c-10.668-2.848-22.536-.087-30.894 8.261-12.48 12.48-12.48 32.78 0 45.26 12.503 12.503 32.803 12.457 45.26 0 8.349-8.359 11.109-20.226 8.261-30.894l20.612-20.612c25.189 20.838 56.548 34.467 90.886 37.727v29.06c-9.56 5.54-16 15.88-16 27.7 0 17.65 14.35 32 32 32s32-14.35 32-32c0-11.82-6.44-22.16-16-27.7v-29.06c34.338-3.26 65.697-16.889 90.886-37.727l20.612 20.612c-2.848 10.668-.087 22.536 8.261 30.894 12.457 12.457 32.757 12.503 45.26 0 12.48-12.48 12.48-32.78 0-45.26-8.359-8.349-20.226-11.109-30.894-8.261l-20.612-20.612c20.838-25.189 34.467-56.548 37.727-90.886h29.06c5.54 9.56 15.88 16 27.7 16 17.65 0 32-14.35 32-32s-14.35-32-32-32zm-277 24c-15.44 0-28-12.56-28-28s12.56-28 28-28 28 12.56 28 28-12.56 28-28 28zm69 96c-17.65 0-32-14.35-32-32s14.35-32 32-32 32 14.35 32 32-14.35 32-32 32zm56-112c-13.23 0-24-10.77-24-24s10.77-24 24-24 24 10.77 24 24-10.77 24-24 24z"/></svg></button>
<button @click.stop="changePassword" class="fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-2 flex bg-blue-500 justify-center items-center text-white rounded-md focus:outline-none"><svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z" clip-rule="evenodd" /></svg></button>
<button @click.stop="deletePatient" class="fill-current mt-2 md:mt-0 p-1 w-1/3 md:w-1/12 ml-2 flex bg-red-500 justify-center items-center text-white rounded-md focus:outline-none"><svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" /></svg></button>
</div>
</div>
</template>


+ 76
- 0
src/components/PatientModal.vue View File

@ -0,0 +1,76 @@
<template>
<div v-if="showModal" class="z-10 modal h-screen w-full fixed left-0 top-0 flex justify-center items-center bg-black bg-opacity-50">
<div class="py-3 sm:max-w-xl sm:mx-auto w-11/12 md:w-1/4">
<div class="relative px-4 py-10 bg-white mx-8 md:mx-0 shadow rounded-3xl sm:p-10">
<div class="max-w-md mx-auto">
<div class="flex items-center space-x-5">
<div class="h-14 w-14 bg-yellow-200 rounded-full flex flex-shrink-0 justify-center items-center text-yellow-500 text-2xl font-mono">{{ patient.name.charAt(0) }}</div>
<div class="block pl-2 font-semibold text-xl self-start text-gray-700">
<h2 class="leading-relaxed">{{ title }}</h2>
<p class="text-sm text-gray-500 font-normal leading-relaxed">Edit patient</p>
</div>
</div>
<div class="divide-y divide-gray-200">
<div class="py-8 text-base leading-6 space-y-4 text-gray-700 sm:text-lg sm:leading-7">
<div class="flex items-center space-x-4">
<div class="w-full flex flex-col">
<label class="leading-loose">Name</label>
<input type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="Patient Name">
</div>
<div class="w-full flex flex-col">
<label class="leading-loose">Phone</label>
<input type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="Patient Name">
</div>
<div class="w-full flex flex-col">
<label class="leading-loose">Phone</label>
<input type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-md focus:outline-none text-gray-600" placeholder="Patient Name">
</div>
</div>
<div class="pt-4 flex items-center space-x-4">
<button @click="showModal = false" class="flex justify-center items-center w-full text-gray-900 px-4 py-3 rounded-md focus:outline-none">
<svg class="w-6 h-6 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg> Cancel
</button>
<button @click="saveClicked" class="bg-blue-500 flex justify-center items-center w-full text-white px-4 py-3 rounded-md focus:outline-none">Create</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "PatientModal",
data: function(){
return {
showModal: false,
patient: {}
}
},
methods: {
showPatient: function(patient){
this.patient = patient;
this.showModal = true;
},
emptyModal: function() {
this.patient = {
name: "",
phone: "",
}
this.showModal = true
},
saveClicked: function(){
this.$emit("savePatient", this.patient);
},
},
computed: {
title: function(){
if(this.patient.name === "")
return "New Patient"
return this.patient.name
}
}
}
</script>

+ 3
- 5
src/components/PatientSearchField.vue View File

@ -26,7 +26,7 @@
<input v-model="email" @input="fieldsUpdated" type="text" class="w-full px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<div class="flex flex-row mt-4">
<div class="flex flex-row w-1/6">
<div class="flex flex-row w-1/4">
<span class="bg-gray-200 border-gray-300 border rounded-l-md p-1 px-2 text-gray-600 flex items-center">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9.243 3.03a1 1 0 01.727 1.213L9.53 6h2.94l.56-2.243a1 1 0 111.94.486L14.53 6H17a1 1 0 110 2h-2.97l-1 4H15a1 1 0 110 2h-2.47l-.56 2.242a1 1 0 11-1.94-.485L10.47 14H7.53l-.56 2.242a1 1 0 11-1.94-.485L5.47 14H3a1 1 0 110-2h2.97l1-4H5a1 1 0 110-2h2.47l.56-2.243a1 1 0 011.213-.727zM9.03 8l-1 4h2.938l1-4H9.031z" clip-rule="evenodd" />
@ -34,12 +34,10 @@
</span>
<input v-model="pageSize" @input="fieldsUpdated" type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<button class="w-1/5 ml-auto flex bg-blue-600 justify-center items-center text-white px-4 rounded-md focus:outline-none">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /></svg> <label class="md:block hidden">New {{ title }}</label>
<button @click="$emit('new')" class="w-1/5 ml-auto flex bg-blue-600 justify-center items-center text-white px-4 rounded-md focus:outline-none">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /></svg> <label class="md:block hidden">New {{ title }}</label>
</button>
</div>
</div>
</template>


src/vue-tailwindcss-typeahead/vue-tailwindcss-typeahead.vue → src/components/Typeahead.vue View File


+ 0
- 74
src/components/UserSearchField.vue View File

@ -1,74 +0,0 @@
<template>
<div class="font-sans">
<div class="flex flex-row w-full mt-4">
<span class="bg-gray-200 border-gray-300 border rounded-l-md p-1 px-2 text-gray-600 flex items-center">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd" />
</svg>
</span>
<input v-model="name" @input="fieldsUpdated" type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<div class="flex flex-row w-full mt-4">
<span class="bg-gray-200 border-gray-300 border rounded-l-md p-1 px-2 text-gray-600 flex items-center">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
</svg>
</span>
<input v-model="phone" @input="fieldsUpdated" type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<div class="flex flex-row w-full mt-4">
<span class="bg-gray-200 border-gray-300 border rounded-l-md p-1 px-2 text-gray-600 flex items-center">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>
</span>
<input v-model="email" @input="fieldsUpdated" type="text" class="w-full px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<div class="flex flex-row mt-4">
<div class="flex flex-row w-1/4">
<span class="bg-gray-200 border-gray-300 border rounded-l-md p-1 px-2 text-gray-600 flex items-center">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M9.243 3.03a1 1 0 01.727 1.213L9.53 6h2.94l.56-2.243a1 1 0 111.94.486L14.53 6H17a1 1 0 110 2h-2.97l-1 4H15a1 1 0 110 2h-2.47l-.56 2.242a1 1 0 11-1.94-.485L10.47 14H7.53l-.56 2.242a1 1 0 11-1.94-.485L5.47 14H3a1 1 0 110-2h2.97l1-4H5a1 1 0 110-2h2.47l.56-2.243a1 1 0 011.213-.727zM9.03 8l-1 4h2.938l1-4H9.031z" clip-rule="evenodd" />
</svg>
</span>
<input v-model="pageSize" @input="fieldsUpdated" type="text" class="px-4 py-2 border focus:ring-gray-500 focus:border-gray-900 w-full sm:text-sm border-gray-300 rounded-r-md focus:outline-none text-gray-600" placeholder="">
</div>
<button class="w-1/5 ml-auto flex bg-blue-600 justify-center items-center text-white px-4 rounded-md focus:outline-none">
<svg class="h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /></svg> <label class="md:block hidden">New {{ title }}</label>
</button>
</div>
</div>
</template>
<script>
export default{
name: 'PatientSearchField',
props: {
title: String
},
// Data
data: function(){
return {
name: "",
email: "",
phone: "",
pageSize: 20
}
},
// Methods
methods: {
fieldsUpdated: function(){
this.$emit('fieldsUpdated', {
name: this.name,
email: this.email,
phone: this.phone,
pageSize: this.pageSize
})
}
}
}
</script>

+ 5
- 5
src/router/index.js View File

@ -5,20 +5,20 @@ Vue.use(VueRouter)
const routes = [
{
path: 'calendar',
path: '/calendar',
name: 'Calendar',
component: () => import('../views/Calendar.vue'),
props: true
},
{
path: 'patients',
path: '/patients',
name: 'Patients',
component: () => import('../views/Patients.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/Login.vue')
path: '/disabled',
name: 'Disabled',
component: () => import('../views/Disabled.vue')
},
]


+ 1
- 1
src/views/Calendar.vue View File

@ -1,7 +1,7 @@
<template>
<div class="calendar w-full h-screen" ref="calendarContainer">
<AppointmentModal @createdEvent="createdEvent" class="z-5" ref="newAppointmentModal"/>
<FullCalendar class="z-0 w-full px-12 py-5" ref="fullCalendar" :options="calendarOptions" />
<FullCalendar class="z-0 w-full px-3 md:px-12 py-5" ref="fullCalendar" :options="calendarOptions" />
<div v-on-clickaway="hidePopover" ref="popoverRef" v-bind:class="{'hidden': !popoverShow, 'block': popoverShow}" class="border-2 w-48 border-gray-200 bg-white border-0 mr-3 block z-50 font-normal leading-normal text-sm text-left no-underline break-words rounded-lg">
<div>
<div data-popper-arrow></div>


+ 70
- 0
src/views/Disabled.vue View File

@ -0,0 +1,70 @@
<template>
<div class="flex-col flex content-center py-6 mx-auto mt-8">
<div class="md:w-4/6 md:mx-auto border-gray-200">
<DisabledCard @save="createRule" class="rounded-md border border-gray-200 p-2 mb-3" :rule="{start: '', duration: '', repetition: ''}" ref="newRule" :nodelete="true"/>
<DisabledCard :nodelete="false" @delete="deleteRule" @save="updateRule" class="rounded-md border border-gray-200 p-2 mb-3" :key="rule.id" v-for="(rule, index) in rules" :rule="rule" :index="index"/>
</div>
</div>
</template>
<script>
import DisabledCard from "@/components/DisabledCard";
import axios from "axios";
export default {
name: "Disabled",
components: {DisabledCard},
data: function(){
return {
rules: []
}
},
methods: {
updateRule: function(rule){
axios.post('http://localhost:9090/disabled/update', rule.obj,{
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('JWT')}`
}
}).then((result) =>{
this.rules[rule.index] = rule.obj;
console.log(result)
})
},
createRule: function(rule){
axios.post('http://localhost:9090/disabled', rule.obj,{
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('JWT')}`
}
}).then((result) =>{
this.rules.push(rule.obj);
this.$refs.newRule.disabled = {start: '', duration: '', repetition: ''}
console.log(result)
})
},
deleteRule: function(rule){
axios.get('http://localhost:9090/disabled/delete', {
params: {
id: rule.obj.id
},
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('JWT')}`
}
}).then((result) =>{
this.rules.splice(rule.index)
console.log(result)
})
},
},
mounted(){
axios.get('http://localhost:9090/disabled/rules', {
headers: {
'Authorization': `Bearer ${window.localStorage.getItem('JWT')}`
}
}).then((result) =>{
console.log(result.data)
this.rules = result.data
}) // TODO err handling here as well
}
}
</script>

+ 2
- 1
src/views/Login.vue View File

@ -7,7 +7,8 @@
<script>
import LoginForm from '@/components/LoginForm.vue';
export default{
export default {
name: "Login",
components : {
LoginForm
}


+ 11
- 4
src/views/Patients.vue View File

@ -1,11 +1,12 @@
<template>
<div class="flex-col flex content-center py-3 mx-auto">
<PatientSearchField class=" md:w-4/6 md:mx-auto" @fieldsUpdated="search" title="Patient"/>
<PatientSearchField @new="showEmptyPatientModal" class=" md:w-4/6 md:mx-auto" @fieldsUpdated="search" title="Patient"/>
<Pagination class="h-10 my-8 mx-auto" :pages="pages" :currentPage="searchFields.page + 1" @page-changed="pageChanged"/>
<div class="md:w-4/6 md:mx-auto md:h-1/4 border-gray-200 md:border-t md:border-b rounded-md md:overflow-auto">
<PatientCard @click="showModal(patient)" @change-password="showPasswordModal" @check-covid="checkCovid" @delete-patient="deletePatient" :class="{'rounded-t-md': index == 0, 'rounded-b-md': index == searchFields.pageSize - 1}" class="border border-gray-200 p-2" :key="patient.id" v-for="(patient, index) in patients" :patient="patient"/>
<PatientCard @showModal="showModal" @change-password="showPasswordModal" @check-covid="checkCovid" @delete-patient="deletePatient" :class="{'rounded-t-md': index == 0, 'rounded-b-md': index == searchFields.pageSize - 1}" class="border border-gray-200 p-2" :key="patient.id" v-for="(patient, index) in patients" :patient="patient"/>
</div>
<Pagination class="h-10 my-8 mx-auto" :pages="pages" :currentPage="searchFields.page + 1" @page-changed="pageChanged"/>
<Pagination class="h-10 my-8 mx-auto" :pages="pages" :currentPage="searchFields.page + 1" @page-changed="pageChanged"/>
<PatientModal class="z-5" ref="patientModal"/>
</div>
</template>
@ -14,10 +15,12 @@ import PatientSearchField from '@/components/PatientSearchField'
import PatientCard from '@/components/PatientCard'
import Pagination from '@/components/Pagination'
import axios from 'axios';
import PatientModal from "@/components/PatientModal";
export default {
name: "Patients",
components: {
PatientModal,
PatientSearchField,
Pagination,
PatientCard
@ -77,7 +80,11 @@ export default {
console.log(patient);
},
showModal: function(patient){
console.log(patient);
this.$refs.patientModal.showPatient(patient);
console.log(patient);
},
showEmptyPatientModal: function(){
this.$refs.patientModal.emptyModal();
}
},
mounted(){


+ 0
- 1
src/vue-tailwind-datetime-picker/css/tailwind.css
File diff suppressed because it is too large
View File


+ 0
- 943
src/vue-tailwind-datetime-picker/vue-tailwind-datetime-picker.vue View File

@ -1,943 +0,0 @@
<template>
<div
ref="vTailwindPickerRef"
id="picker"
class="relative select-none"
v-closable="{
handler: 'onAway',
exclude: ['currentPicker', 'time'],
}"
@click.prevent="onFeedBack"
:style="`--bg-tailwind-picker: ${theme.background};`"
>
<slot></slot>
<transition name="v-tailwind-picker">
<div v-show="showPicker || inline">
<div
id="v-tailwind-picker"
class="bg-transparent mt-3 z-30"
:class="[
{ 'inline-mode': inline },
inline ? 'static' : 'absolute bottom-0 inset-x-0',
theme.currentColor,
]"
>
<div
class="w-88 h-auto max-w-xs transition-all duration-150 ease-linear rounded overflow-hidden border"
:class="[
theme.border,
theme.text,
inline ? 'shadow-xs' : 'shadow-md',
]"
:style="{ backgroundColor: `var(--bg-tailwind-picker)` }"
>
<!-- Header of picker-->
<div id="v-tailwind-picker-header" class="hidden">
<div class="flex flex-row justify-center items-center px-2 py-1">
<div class="flex items-center text-2xl xl:text-3xl">
{{ today.locale($props.lang).format('DD') }}
</div>
<div class="mx-1">
<div class="leading-none text-xxs">
{{ today.locale($props.lang).format('dddd') }}
</div>
<div class="leading-none text-xs">
{{ today.locale($props.lang).format('MMMM YYYY') }}
</div>
</div>
</div>
</div>
<!-- Navigation of picker-->
<div class="relative p-2 pb-3">
<div
class="absolute inset-0"
:class="theme.navigation.background"
></div>
<div class="flex justify-between items-center relative">
<div class="flex-shrink-0 w-8">
<transition name="v-tailwind-picker-chevron-left">
<div
v-if="!enableMonth && !enableYear"
class="rounded-full overflow-hidden"
>
<div
class="transition duration-150 ease-out p-2"
:class="[
visiblePrev
? 'cursor-pointer'
: 'cursor-not-allowed opacity-30',
theme.navigation.hover,
]"
@click="onPrevious()"
>
<svg
class="h-4 w-auto"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 511.641 511.641"
fill="currentColor"
>
<path
d="M148.32 255.76L386.08 18c4.053-4.267 3.947-10.987-.213-15.04a10.763 10.763 0 00-14.827 0L125.707 248.293a10.623 10.623 0 000 15.04L371.04 508.667c4.267 4.053 10.987 3.947 15.04-.213a10.763 10.763 0 000-14.827L148.32 255.76z"
/>
</svg>
</div>
</div>
</transition>
</div>
<div class="flex flex-1">
<div
class="flex-1 border-b-2 overflow-hidden py-1 ml-4 mr-6 text-center cursor-pointer transition duration-150 ease-out"
:class="[
enableMonth ? theme.navigation.focus : '',
theme.navigation.hover, theme.time.border
]"
@click="toggleMonth()"
>
{{ today.locale($props.lang).format('MMMM') }}
</div>
<div
class="flex-1 border-b-2 overflow-hidden py-1 mr-4 ml-6 text-center cursor-pointer transition duration-150 ease-out"
:class="[
enableYear ? theme.navigation.focus : '',
theme.navigation.hover, theme.time.border
]"
@click="toggleYear()"
>
{{ today.$y }}
</div>
</div>
<div class="flex-shrink-0 w-8">
<transition name="v-tailwind-picker-chevron-right">
<div
v-if="!enableMonth && !enableYear"
class="rounded-full overflow-hidden"
>
<div
class="transition duration-150 ease-out p-2"
:class="[
visibleNext
? 'cursor-pointer'
: 'cursor-not-allowed opacity-30',
theme.navigation.hover,
]"
@click="onNext()"
>
<svg
class="h-4 w-auto"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 511.949 511.949"
fill="currentColor"
>
<path
d="M386.235 248.308L140.902 2.975c-4.267-4.053-10.987-3.947-15.04.213a10.763 10.763 0 000 14.827l237.76 237.76-237.76 237.867c-4.267 4.053-4.373 10.88-.213 15.04 4.053 4.267 10.88 4.373 15.04.213l.213-.213 245.333-245.333a10.624 10.624 0 000-15.041z"
/>
</svg>
</div>
</div>
</transition>
</div>
</div>
</div>
<!-- Body of picker-->
<div class="overflow-x-hidden border-t border-b p-3 overflow-y-auto" :class="[theme.borders.accent]">
<transition name="v-tailwind-picker-body">
<div v-if="!enableMonth && !enableYear" class="relative">
<div
class="flex flex-no-wrap py-2 border-b"
:class="[theme.border, theme.borders.accent]"
>
<div
v-for="day in days"
:key="day"
class="w-1/7 flex justify-center"
>
<div class="leading-relaxed text-sm">
{{ day }}
</div>
</div>
</div>
<div ref="currentPicker" class="flex flex-wrap relative">
<div
v-for="(date, i) in previousPicker"
:key="`${date.$D}${date.$M}${date.$y}-previous`"
class="w-1/7 flex justify-center my-2px cursor-not-allowed"
:class="[
i === previousPicker.length - 1 ? 'rounded-r-full' : '',
theme.navigation.backgroundoverlapse,
]"
>
<div
class="h-8 w-8 flex justify-center items-center"
:data-tailwind-datepicker="date.$d"
>
<div
class="text-xs opacity-75"
:class="[
date.day() === 0
? theme.picker.holiday
: date.day() === 5
? theme.picker.weekend
: '',
]"
>
{{ date.$D }}
</div>
</div>
</div>
<div
v-for="date in currentPicker"
:key="`${date.$D}${date.$M}${date.$y}-current`"
class="w-1/7 group flex justify-center items-center my-2px"
>
<div
class="relative overflow-hidden"
:class="theme.picker.rounded"
>
<div
v-if="date.$events"
class="absolute top-0 right-0 h-2 w-2 z-2"
:class="theme.picker.event"
:style="{
backgroundColor: date.$events.color
? date.$events.color
: '',
}"
></div>
<div
class="relative flex justify-center items-center overflow-hidden"
:class="[
theme.picker.rounded,
possibleDate(date)
? 'cursor-pointer'
: 'cursor-not-allowed',
]"
>
<div @click="changePicker(date)" :data-tailwind-datepicker="date.$d">
<div class="flex justify-center items-center h-8 w-8"
:class="[
possibleDate(date) && date.$D === today.$D
? `${theme.picker.selected.background} z-10 text-white`
: `hover:${theme.picker.hovered.background}`,
(holidayDate(date) || date.day() === 0) && date.$D !== today.$D
? theme.picker.holiday
: '',
date.day() === 5 && date.$D !== today.$D
? theme.picker.weekend
: '',
{
'opacity-50': !possibleDate(date),
}
]"
>
<span>{{ date.$D }}</span>
</div>
</div>
</div>
</div>
</div>
<div
v-for="date in nextPicker"
:key="`${date.$D}${date.$M}${date.$y}-next`"
class="w-1/7 flex justify-center my-2px cursor-not-allowed"
:class="[
date.$D === 1 ? 'rounded-l-full' : '',
theme.navigation.backgroundoverlapse,
]"
>
<div
class="h-8 w-8 flex justify-center items-center"
:data-tailwind-datepicker="date.$d"
>
<div
class="text-xs opacity-75"
:class="[
date.day() === (startFromMonday ? 1 : 0)
? theme.picker.holiday
: date.day() === (startFromMonday ? 6 : 5)
? theme.picker.weekend
: '',
]"
>
{{ date.$D }}
</div>
</div>
</div>
</div>
</div>
</transition>
<transition name="v-tailwind-picker-months">
<div
v-if="enableMonth"
class="relative flex items-center smooth-scrolling overflow-y-auto overflow-x-hidden"
>
<div class="flex flex-wrap py-1">
<div
v-for="(month, i) in months"
:key="i"
class="w-1/3 flex justify-center items-center px-2"
>
<div
:class="[i === today.$M ? `${theme.picker.selected.border}` : `${theme.border} ${theme.picker.selected.hover}`]"
class="w-full flex justify-center items-center py-2 my-1 transition duration-150 ease-out rounded border cursor-pointer"
@click="setMonth(i)"
>
<span class="font-medium">{{ month }}</span>
</div>
</div>
</div>
</div>
</transition>
<transition name="v-tailwind-picker-years">
<div
v-if="enableYear"
class="relative smooth-scrolling overflow-y-auto overflow-x-hidden"
>
<div class="flex flex-wrap py-1">
<div
v-for="(year, i) in years"
:key="i"
class="w-1/3 flex justify-center items-center px-2"
>
<div
:class="[
year === today.$y
? `${theme.picker.selected.border}`
: `${theme.border} ${theme.picker.selected.hover}`,
]"
class="w-full flex justify-center items-center py-2 my-1 transition duration-150 ease-out rounded border cursor-pointer"
@click="setYear(year)"
>
<span class="font-medium">{{ year }}</span>
</div>
</div>
</div>
</div>
</transition>
</div>
<!-- Event of picker-->
<div
v-if="
currentPicker.filter((o) => o.$events !== undefined).length > 0
"
id="v-tailwind-picker-footer"
>
<transition-group
name="v-tailwind-picker-footer"
tag="div"
class="flex flex-wrap border-t p-1"
:class="theme.event.border"
>
<div
v-for="(event, i) in currentPicker.filter(
(o) => o.$events !== undefined,
)"
:key="`${i}-event`"
class="w-full flex flex-row space-x-1 mb-px"
>
<div class="inline-flex justify-end w-4">
<span class="text-xs leading-none" :class="theme.picker.holiday">
{{ dayjs(event.$events.date, formatDate).$D }}
</span>
</div>
<div class="flex flex-wrap">
<div class="w-full flex items-end">
<span class="text-xxs leading-none">
{{ event.$events.description }}
</span>
</div>
</div>
</div>
</transition-group>
</div>
<div ref="time" class="mt-1 p-1 bg-white shadow-xl" v-if="selecttime">
<div class="flex justify-center border-b-2 pt-2 mx-24 mb-8 m-2" :class="[theme.time.border]">
<select name="hours" v-model="hours" class="bg-transparent font-medium text-xl appearance-none outline-none">
<option :value="(hour-1).toString()" @click="setHour(hour-1)" v-for="hour in 25" :key="hour">{{ pad(hour-1) }}</option>
</select>
<span class="text-xl mr-1 ml-1">:</span>
<select name="minutes" class="bg-transparent text-xl font-medium appearance-none outline-none">
<option :value="(minute-1).toString()" @click="setMinute(minute-1)" :key="minute" v-for="minute in 60">{{ pad(minute-1) }}</option>
</select>
</div>
<hr>
<div class="flex justify-center mb-2 mt-4">
<button class="bg-transparent text-xl px-12 appearance-none outline-none" @click="hide()" :class="[`${theme.picker.selected.background} ${theme.picker.rounded} text-white`]">
OK
</button>
</div>
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
<script id="alpha">
/**
* Author: kenhyuwa <wahyu.dhiraashandy8@gmail.com
* Url: https://github.com/kenhyuwa
**/
import dayjs from 'dayjs'
import isToday from 'dayjs/plugin/isToday'
import isBetween from 'dayjs/plugin/isBetween'
import dynamicLocale from 'dayjs-dynamic-locale'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import localizedFormat from 'dayjs/plugin/localizedFormat'
import advancedFormat from 'dayjs/plugin/advancedFormat'
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
dayjs.extend(isToday)
dayjs.extend(isBetween)
dayjs.extend(dynamicLocale)
dayjs.extend(customParseFormat)
dayjs.extend(localizedFormat)
dayjs.extend(advancedFormat)
dayjs.extend(isSameOrBefore)
dayjs.extend(isSameOrAfter)
let handleOutsideClick
export default {
name: 'VueTailwindDatetimePicker',
beforeCreated() {
dayjs.locale(this.$props.lang)
},
directives: {
closable: {
// https://github.com/TahaSh/vue-closable // resource
bind(el, binding, vnode) {
// Here's the click/touchstart handler
// (it is registered below)
handleOutsideClick = (e) => {
e.stopPropagation()
// Get the handler method name and the exclude array
// from the object used in v-closable
const { handler, exclude } = binding.value
// This variable indicates if the clicked element is excluded
let clickedOnExcludedEl = false
if (exclude) {
exclude.forEach((refName) => {
// We only run this code if we haven't detected
// any excluded element yet
if (!clickedOnExcludedEl) {
// Get the element using the reference name
const excludedEl = vnode.context.$refs[refName]
// See if this excluded element
// is the same element the user just clicked on
clickedOnExcludedEl = excludedEl
? excludedEl.contains(e.target)
: false
}
})
}
// We check to see if the clicked element is not
// the dialog element and not excluded
if (clickedOnExcludedEl && ( vnode.context.closed || vnode.context.autoClose)) {
vnode.context[handler]()
}
if (!el.contains(e.target) && !clickedOnExcludedEl) {
// If the clicked element is outside the dialog
// and not the button, then call the outside-click handler
// from the same component this directive is used in
vnode.context[handler]()
}
}
// Register click/touchstart event listeners on the whole page
document.addEventListener('click', handleOutsideClick)
document.addEventListener('touchstart', handleOutsideClick)
},
unbind() {
// If the element that has v-closable is removed, then
// unbind click/touchstart listeners from the whole page
document.removeEventListener('click', handleOutsideClick)
document.removeEventListener('touchstart', handleOutsideClick)
},
},
},
props: {
lang: {
type: String,
required: false,
default: "en"
},
selecttime: {
type: Boolean,
required: false,
default: true
},
init: {
type: Boolean,
required: false,
default: true,
},
selectedDate: {
type: String,
required: false,
},
startDate: {
type: String,
required: false,
default: dayjs().format('YYYY-MM-DDTHH:mm'),
},
endDate: {
type: String,
required: false,
default: undefined,
},
// Next future
disableDate: {
type: Array,
required: false,
default: () => [],
},
eventDate: {
type: Array,
required: false,
default: () => [],
},
formatDate: {
type: String,
required: false,
default: 'YYYY-MM-DDTHH:mmZ',
},
// Confused with this
formatDisplay: {
type: String,
required: false,
default: 'YYYY-MM-DD',
},
inline: {
type: Boolean,
required: false,
default: false,
},
// Not make sure with this
tailwindPickerValue: {
type: String,
required: false,
default: '',
},
// Next future
dateRange: {
type: Boolean,
required: false,
default: true,
},
// Next future
autoCloseProp: {
type: Boolean,
required: false,
default: false,
},
startFromMonday: {
type: Boolean,
required: false,
default: true,
},
theme: {
type: Object,
required: false,
default: () => ({
background: '#ffffff',
text: 'text-gray-700',
border: 'border-gray-200',
time: {
border: 'border-blue-300'
},
borders: {
accent: 'border-blue-200'
},
currentColor: 'text-gray-200',
navigation: {
background: 'bg-gray-100',
backgroundoverlapse: 'bg-gray-100',
hover: 'hover:bg-gray-200',
focus: 'bg-gray-200'
},
picker: {
rounded: 'rounded',
selected: {
background: 'bg-blue-700',
border: 'border-transparent',
hover: 'hover:bg-gray-200'
},
hovered: {
background: 'bg-gray-300'
},
holiday: 'text-gray-700',
weekend: 'text-gray-700',
event: 'bg-blue-700'
},
event: {
border: 'border-none'
}
})
}
},
data() {
const startDatepicker = dayjs(this.startDate, this.formatDate)
const endDatepicker = this.endDate ? dayjs(this.endDate, this.formatDate) : undefined
const today = this.selectedDate && this.startDate < this.selectedDate ? dayjs(this.selectedDate, this.formatDate) : dayjs(startDatepicker, this.formatDate);
const months = Array.from(Array(12), (v, i) => {
return dayjs().month(i).locale(this.$props.lang).format('MMMM')
})
const years = Array.from(
Array(this.endDate ? endDatepicker.diff(today, 'year') + 1 : today.diff(today, 'year') + 5),
(v, i) => {
return today.add(i, 'year').$y
}
)
const visibleMonth = false
const visibleYear = false
const showPicker = false
return {
startDatepicker,
endDatepicker,
visibleMonth,
visibleYear,
showPicker,
months,
years,
hours: 12,
today,
closed: false
}
},
computed: {
autoClose() {
if (!this.selecttime) {
return this.autoCloseProp
} else {
return false
}
},
days() {
const customWeekend = this.startFromMonday ? 1 : 0
return Array.from(Array(7), (v, i) => {
return dayjs().day(i + customWeekend).locale(this.$props.lang).format('ddd')
})
},
previousPicker() {
const customWeekend = this.startFromMonday ? 1 : 0
const display = []
const previous = this.today.date(0)
const current = this.today.date(0)
for (let i = 0; i <= current.day() - customWeekend; i++) {
display.push(previous.subtract(i, 'day'))
}
return display.sort((a, b) => a.$d - b.$d)
},
currentPicker() {
const customWeekend = this.startFromMonday ? 1 : 0
const eventDate = this.eventDate.length > 0 ? this.eventDate : []
return Array.from(
Array(this.today.daysInMonth() - customWeekend),
(v, i) => {
const events = this.today.date(++i)
events.$events = eventDate.find((o) => {
return o.date === events.locale(this.$props.lang).format(this.formatDate)
})
return events
}
)
},
nextPicker() {
const customWeekend = this.startFromMonday ? 1 : 0
const display = []
const previous = this.previousPicker.length
const current = this.today.daysInMonth()
for (let i = 1; i <= 42 - (previous + current) + customWeekend; i++) {
display.push(this.today.date(i).add(1, 'month'))
}
return display
},
enableMonth() {
return this.visibleMonth
},
enableYear() {
return this.visibleYear
},
visiblePrev() {
if (!this.dateRange) {
const endOf = this.today.subtract(1, 'month').endOf('month')
const diff = this.startDatepicker.diff(endOf, 'day')
return diff < this.today.daysInMonth() - this.today.$D
}
return true
},
visibleNext() {
if (!this.dateRange && this.endDate) {
const startOf = this.today.add(1, 'month').startOf('month')
return this.endDatepicker.diff(startOf, 'day') > 0
}
return true
}
},
watch: {
showPicker(prev, next) {
if (prev) {
this.visibleMonth = next
this.visibleYear = next
}
}
},
mounted() {
if (this.init) {
this.setHour(this.hours)
this.setMinute(0)
this.emit()
}
},
methods: {
hide() {
this.closed = true
this.showPicker = false
},
pad(nr) {
return ('0' + nr.toString()).slice(-2)
},
emit() {
this.$emit('change', this.today.format(this.formatDate))
},
changePicker(date) {
this.today = date
this.emit()
this.showPicker = !this.showPicker
},
onPrevious() {
if (this.visiblePrev) {
const today = this.today
.set('month', this.today.$M === 0 ? 11 : this.today.$M - 1)
.set('year', this.today.$M === 0 ? this.today.$y - 1 : this.today.$y)
if (this.possibleDate(today)) {
this.today = today
} else {
this.today = this.startDatepicker
}
this.emit()
}
},
onNext() {
if (this.visibleNext) {
const today = this.today
.set('month', (this.today.$M + 1) % 12)
.set('year', this.today.$M === 11 ? this.today.$y + 1 : this.today.$y)
if (this.possibleDate(today)) {
this.today = today
} else {
this.today = this.endDatepicker
}
this.emit()
}
},
possibleStartDate(date) {
return this.dateRange
? true
: date.isSameOrAfter(this.startDatepicker, 'day')
},
possibleEndDate(date) {
if (this.endDate) {
return this.dateRange
? true
: date.isSameOrBefore(this.endDatepicker, 'day')
}
return false
},
possibleDate(date) {
if (this.endDate) {
return this.possibleStartDate(date) && this.possibleEndDate(date)
}
return this.possibleStartDate(date)
},
holidayDate(date) {
return !!(date.$events && date.$events.holiday)
},
toggleMonth() {
this.visibleMonth = !this.visibleMonth
if (this.visibleMonth) {
this.visibleYear = false
}
},
toggleYear() {
this.visibleYear = !this.visibleYear
if (this.visibleYear) {
this.visibleMonth = false
}
},
setMonth(month) {
if (this.possibleDate(this.today.set('month', month))) {
this.today = this.today.set('month', month)
} else {
this.today = this.startDatepicker
}
this.emit()
this.toggleMonth()
},
setHour(hour) {
this.today = this.today.set('hour', hour)
this.emit()
},
setMinute(minute) {
this.today = this.today.set('minute', minute)
this.emit()
},
setYear(year) {
if (this.possibleDate(this.today.set('year', year))) {
this.today = this.today.set('year', year)
} else {
this.today = this.startDatepicker
}
this.emit()
this.toggleYear()
},
onAway() {
this.showPicker = false
},
onFeedBack() {
if (!closed) {
this.showPicker = true
} else {
this.showPicker = false
}
}
}
}
</script>
<!--
<style>
@import './css/tailwind.css'; /* Development only */
</style>
-->
<style scoped>
#v-tailwind-picker {
top: 95%;
}
#v-tailwind-picker .w-1\/7 {
width: 14.285714%;
}
#v-tailwind-picker .w-88 {
width: 20rem;
}
#v-tailwind-picker .text-xxs {
font-size: 0.6rem;
}
#v-tailwind-picker .my-2px {
margin-top: 2px;
margin-bottom: 2px;
}
#v-tailwind-picker:not(.inline-mode)::before {
content: '';
width: 14px;
height: 14px;
z-index: 10;
top: -7px;
left: 12px;
border-radius: 2px;
border-color: currentColor;
position: absolute;
display: block;
background-color: var(--bg-tailwind-picker);
border-left-width: 1px;
border-top-width: 1px;
transform: rotate(45deg);
}
#v-tailwind-picker .smooth-scrolling {
height: 255px;
max-height: 255px;
}
#v-tailwind-picker .smooth-scrolling::-webkit-scrollbar {
width: 4px;
}
#v-tailwind-picker .smooth-scrolling ::-webkit-scrollbar-thumb {
border-radius: 8px;
background-color: rgba(0, 0, 0, 0.1);
}
.v-tailwind-picker-enter-active,
.v-tailwind-picker-leave-active {
transition: all 0.1s;
}
.v-tailwind-picker-enter,
.v-tailwind-picker-leave-to {
opacity: 0;
transform: translateY(15px);
}
.v-tailwind-picker-body-enter-active,
.v-tailwind-picker-body-leave-active {
transition: all 0.2s;
}
.v-tailwind-picker-body-enter,
.v-tailwind-picker-body-leave-to {
opacity: 0;
transform: translateY(-15px);
}
.v-tailwind-picker-months-enter-active,
.v-tailwind-picker-months-leave-active {
transition: all 0.2s;
}
.v-tailwind-picker-months-enter,
.v-tailwind-picker-months-leave-to {
opacity: 0;
transform: translateY(-15px);
}
.v-tailwind-picker-years-enter-active,
.v-tailwind-picker-years-leave-active {
transition: all 0.2s;
}
.v-tailwind-picker-years-enter,
.v-tailwind-picker-years-leave-to {
opacity: 0;
transform: translateY(-15px);
}
.v-tailwind-picker-footer-enter-active,
.v-tailwind-picker-footer-leave-active {
transition: all 0.2s;
}
.v-tailwind-picker-footer-enter,
.v-tailwind-picker-footer-leave-to {
opacity: 0;
transform: translateY(-15px);
}
</style>

+ 0
- 28
src/vue-tailwind-datetime-picker/wrapper.js View File

@ -1,28 +0,0 @@
// Import vue component
import component from './vue-tailwind-datetime-picker.vue'
// Declare install function executed by Vue.use()
export function install(Vue) {
if (install.installed) return
install.installed = true
Vue.component('VueTailwindDatetimePicker', component)
}
// Create module definition for Vue.use()
const plugin = {
install,
}
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null
if (typeof window !== 'undefined') {
GlobalVue = window.Vue
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue
}
if (GlobalVue) {
GlobalVue.use(plugin)
}
// To allow use as module (npm/webpack/etc.) export component
export default component

+ 0
- 5
src/vue-tailwindcss-typeahead/css/tailwind.css View File

@ -1,5 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

+ 0
- 40
src/vue-tailwindcss-typeahead/entry.js View File

@ -1,40 +0,0 @@
// Import vue component
import component from '@/vue-tailwindcss-typeahead.vue';
// install function executed by Vue.use()
const install = function installVueTailwindcssTypeahead(Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('VueTailwindcssTypeahead', component);
};
// Create module definition for Vue.use()
const plugin = {
install,
};
// To auto-install on non-es builds, when vue is found
// eslint-disable-next-line no-redeclare
/* global window, global */
if ('false' === process.env.ES_BUILD) {
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = global.Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
}
// Inject install function into component - allows component
// to be registered via Vue.use() as well as Vue.component()
component.install = install;
// Export component by default
export default component;
// It's possible to expose named exports when writing components that can
// also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo';
// export const RollupDemoDirective = component;

Loading…
Cancel
Save