add file
This commit is contained in:
4
.env.development
Normal file
4
.env.development
Normal file
@ -0,0 +1,4 @@
|
||||
VITE_BASE_URL=https://api.domain.com/v1/
|
||||
VITE_BASE_DIRECTORY=/
|
||||
VITE_APP_VERSION=0.0.1
|
||||
VITE_APP_NAME='Executive Information System'
|
4
.env.production
Normal file
4
.env.production
Normal file
@ -0,0 +1,4 @@
|
||||
VITE_BASE_URL=https://api.domain.com/v1/
|
||||
VITE_BASE_DIRECTORY=/
|
||||
VITE_APP_VERSION=0.0.1
|
||||
VITE_APP_NAME='Executive Information System'
|
877
package-lock.json
generated
877
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "apkt",
|
||||
"version": "0.0.0",
|
||||
"name": "eis",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
@ -25,7 +25,9 @@
|
||||
"uuid": "^9.0.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.2",
|
||||
"vue-tailwind-datepicker": "^1.4.5"
|
||||
"vue-tailwind-datepicker": "^1.4.5",
|
||||
"encrypt-storage": "^2.12.16",
|
||||
"dotenv": "^16.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.5.1",
|
||||
|
@ -762,6 +762,10 @@ select {
|
||||
left: -0.5rem;
|
||||
}
|
||||
|
||||
.bottom-0 {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.left-0 {
|
||||
left: 0px;
|
||||
}
|
||||
@ -802,34 +806,6 @@ select {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.top-\[50\%\] {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.bottom-0 {
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.\!bottom-0 {
|
||||
bottom: 0px !important;
|
||||
}
|
||||
|
||||
.\!right-0 {
|
||||
right: 0px !important;
|
||||
}
|
||||
|
||||
.\!top-0 {
|
||||
top: 0px !important;
|
||||
}
|
||||
|
||||
.bottom-auto {
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
.top-auto {
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
@ -867,6 +843,11 @@ select {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.mx-3 {
|
||||
margin-left: 0.75rem;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.mx-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
@ -887,26 +868,6 @@ select {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.mx-4 {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.mx-3 {
|
||||
margin-left: 0.75rem;
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.\!mx-3 {
|
||||
margin-left: 0.75rem !important;
|
||||
margin-right: 0.75rem !important;
|
||||
}
|
||||
|
||||
.\!my-auto {
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
|
||||
.\!mt-0 {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
@ -1107,6 +1068,11 @@ select {
|
||||
height: 74vh;
|
||||
}
|
||||
|
||||
.h-fit {
|
||||
height: -moz-fit-content;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
@ -1115,11 +1081,6 @@ select {
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.h-fit {
|
||||
height: -moz-fit-content;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.max-h-0 {
|
||||
max-height: 0px;
|
||||
}
|
||||
@ -1498,10 +1459,6 @@ select {
|
||||
--tw-divide-opacity: 0.1;
|
||||
}
|
||||
|
||||
.justify-self-center {
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const router = createRouter({
|
||||
linkActiveClass: 'active',
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
'meta': { requiresAuth: true },
|
||||
component: HomeView,
|
||||
@ -295,9 +295,9 @@ const router = createRouter({
|
||||
path: '/logout',
|
||||
name: 'Logout',
|
||||
beforeEnter(to, from, next) {
|
||||
const auth = useAuthStore();
|
||||
auth.logout();
|
||||
next('/login');
|
||||
const auth = useAuthStore()
|
||||
auth.logout()
|
||||
next('/login')
|
||||
},
|
||||
redirect: '/logout'
|
||||
},
|
||||
@ -319,26 +319,36 @@ const router = createRouter({
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach((to, _from) => {
|
||||
router.beforeEach((to, from, next) => {
|
||||
// redirect to login page if not logged in and trying to access a restricted page
|
||||
const publicPages = ['/login'];
|
||||
const authRequired = !publicPages.includes(to.path);
|
||||
const auth = useAuthStore();
|
||||
|
||||
// if to is not found, redirect to 404
|
||||
if (to.matched.length === 0) {
|
||||
return '/404';
|
||||
if (to.path === '/') {
|
||||
if (auth.isLoggedIn) {
|
||||
next('/home')
|
||||
} else {
|
||||
// if to is not login and user is not logged in, redirect to login
|
||||
if (!auth.isLoggedIn) {
|
||||
// if to is 404, redirect to 404
|
||||
if (to.path !== '/404' && to.path !== '/login') {
|
||||
return '/login';
|
||||
next('/login')
|
||||
}
|
||||
} else {
|
||||
next('/404')
|
||||
}
|
||||
} else {
|
||||
// if to is not login and user is not logged in, redirect to login
|
||||
if (auth.isLoggedIn) {
|
||||
// if to is login and user is logged in, redirect to home
|
||||
if (to.path === '/login') {
|
||||
return '/';
|
||||
next('/home')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
// if to is 404, redirect to 404
|
||||
if (to.path !== '/404' && to.path !== '/login') {
|
||||
next('/login')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { dispatchNotification } from '@/components/Notification'
|
||||
import { readData, removeData, writeData } from './storage'
|
||||
import router from '@/router'
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
// token from localStorage
|
||||
const token = ref(localStorage.getItem('token') || '')
|
||||
const token = ref(readData('token') || '')
|
||||
// create a shared state
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
@ -13,7 +14,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
const isLoggedIn = computed(() => token.value !== '')
|
||||
|
||||
// define your actions
|
||||
function login() {
|
||||
const login = () => {
|
||||
if (username.value == '' || password.value == '') {
|
||||
dispatchNotification({ title: 'Perhatian', content: 'Username atau password tidak boleh kosong', type: 'warning' })
|
||||
} else {
|
||||
@ -22,11 +23,10 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
isLoading.value = false
|
||||
if (username.value == 'demo' && password.value == 'demo') {
|
||||
// store token in localStorage
|
||||
localStorage.setItem('token', 'secret-token')
|
||||
writeData('token', 'secret-token')
|
||||
dispatchNotification({ title: 'Berhasil', content: 'Login berhasil, selamat datang kembali!', type: 'success' })
|
||||
// redirect to home page after login
|
||||
// router.replace('/')
|
||||
router.go(0)
|
||||
router.push('/home')
|
||||
} else {
|
||||
dispatchNotification({ title: 'Login Gagal', content: 'Username atau password salah', type: 'error' })
|
||||
}
|
||||
@ -34,10 +34,15 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
const logout = () => removeData('token')
|
||||
|
||||
// expose everything
|
||||
return { token, username, password, isLoggedIn, login, logout, isLoading }
|
||||
return {
|
||||
token,
|
||||
username,
|
||||
password,
|
||||
isLoggedIn,
|
||||
isLoading,
|
||||
login,
|
||||
logout,
|
||||
}
|
||||
})
|
28
src/stores/storage.ts
Normal file
28
src/stores/storage.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { EncryptStorage } from 'encrypt-storage'
|
||||
const encryptStorage = new EncryptStorage('qwertyuiopasdfghjklzxcvbnm-1234567890')
|
||||
|
||||
const writeData = (key: string, data: any) => encryptStorage.setItem(key, data)
|
||||
const writeDataJson = (key: string, data: any) => encryptStorage.setItem(key, JSON.stringify(data))
|
||||
const removeData = (key: string) => encryptStorage.removeItem(key)
|
||||
const readData = (key: string) => {
|
||||
try {
|
||||
return encryptStorage.getItem(key)
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
const readDataJson = (key: string) => {
|
||||
try {
|
||||
return encryptStorage.getItem(key)
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
removeData,
|
||||
writeData,
|
||||
writeDataJson,
|
||||
readData,
|
||||
readDataJson
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import Icon from '@/assets/images/pln-with-text.png';
|
||||
import Hero from '@/assets/images/hero.png';
|
||||
import Button from '@/components/ButtonPrimary.vue';
|
||||
import InputText from '@/components/InputText.vue';
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import Icon from '@/assets/images/pln-with-text.png'
|
||||
import Hero from '@/assets/images/hero.png'
|
||||
import Button from '@/components/ButtonPrimary.vue'
|
||||
import InputText from '@/components/InputText.vue'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const authStore = useAuthStore()
|
||||
|
||||
onMounted(() => {
|
||||
window.document.title = `Login - ${import.meta.env.VITE_APP_NAME}`
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -35,7 +40,6 @@ const authStore = useAuthStore();
|
||||
<img :src="Hero" alt="logo" class="h-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user