diff --git a/package-lock.json b/package-lock.json index 0607ceb5ba6dd73e90ef38b932146c76f8a2fa18..48f47d2a68c7625508106ca651eeac7650da8e24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "core-js": "^3.6.5", "jquery": "^3.6.0", "vue": "^3.0.0", - "vue-router": "^4.0.12" + "vue-router": "^4.0.12", + "vue-select": "^4.0.0-beta.3" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", @@ -14019,6 +14020,14 @@ "vue": "^3.0.0" } }, + "node_modules/vue-select": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.3.tgz", + "integrity": "sha512-sXufbTDluz26a39UUZE9jeh9Hojnn8zr9qDDUlVEBc7E0F+h3+Nzy8YzyFHuVNrrYLmK0zrPkb7hWFulAEW41Q==", + "peerDependencies": { + "vue": "3.x" + } + }, "node_modules/vue-style-loader": { "version": "4.1.3", "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", @@ -26607,6 +26616,12 @@ "@vue/devtools-api": "^6.0.0-beta.18" } }, + "vue-select": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.3.tgz", + "integrity": "sha512-sXufbTDluz26a39UUZE9jeh9Hojnn8zr9qDDUlVEBc7E0F+h3+Nzy8YzyFHuVNrrYLmK0zrPkb7hWFulAEW41Q==", + "requires": {} + }, "vue-style-loader": { "version": "4.1.3", "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", diff --git a/package.json b/package.json index e0e5151d372051ae0fa0ffed26276e4e02a87deb..997dd32021d8e81eb96b522b3de7b263fbd17f2d 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "core-js": "^3.6.5", "jquery": "^3.6.0", "vue": "^3.0.0", - "vue-router": "^4.0.12" + "vue-router": "^4.0.12", + "vue-select": "^4.0.0-beta.3" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", diff --git a/src/App.vue b/src/App.vue index b161d4906d3407983920455226cafb2550b2f86f..94d49230e0c8f941740fa1ebcbc8d41484aced92 100644 --- a/src/App.vue +++ b/src/App.vue @@ -30,6 +30,14 @@ body { color: #6992b4; } +.bground-light { + background-color: #f1f7fc; +} + +.bground-dark { + background-color: #6992b4; +} + .text-red { color: #f4476b; } diff --git a/src/components/commentCard.vue b/src/components/commentCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..adb544912912e856ee8ec491865df5a4174baf8a --- /dev/null +++ b/src/components/commentCard.vue @@ -0,0 +1,34 @@ +<template> + <div class="card mx-3 my-1 bground-light"> + <div class="card-body"> + <p class="card-subtitle text-blue mt-1">{{comment.comment}}</p> + </div> + + <div class="card-footer"> + <p class=" text-blue">{{comment.created_at}}</p> + </div> + </div> +</template> + +<script> +export default { + name: "CommentCard", + props: { + comment : Object + }, + data() { + return { + + }; + }, + methods: { + + }, +}; +</script> + +<style scoped> +#footer{ + width: 100%; +} +</style> diff --git a/src/components/kpiCard.vue b/src/components/kpiCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..e63b0f6bd4e0df534b8787f706b1d02d38997f1b --- /dev/null +++ b/src/components/kpiCard.vue @@ -0,0 +1,39 @@ +<template> + <div class="card mx-3 my-1 bground-light"> + <div class="card-body"> + <h5 class="card-title">{{kpi.name}}</h5> + <p class="card-subtitle text-blue">{{kpi.description}}</p> + <p class="card-subtitle text-blue mt-1">Deadline: {{kpi.deadline_time}}</p> + </div> + + <div class="card-footer"> + <ul class="list-group list-group-horizontal" id="footer"> + <li class="list-group-item bground-light text-blue">Target: {{kpi.target}}</li> + <li class="list-group-item bground-light text-blue">Weight: {{kpi.weight}}</li> + </ul> + </div> + </div> +</template> + +<script> +export default { + name: "KpiCard", + props: { + kpi : Object + }, + data() { + return { + + }; + }, + methods: { + + }, +}; +</script> + +<style scoped> +#footer{ + width: 100%; +} +</style> diff --git a/src/components/sidebar.vue b/src/components/sidebar.vue index 33c34473ade6058037f041d69d6bb28cf075636e..a1ba89c257d53cc4863cbf62308282efa541b7f0 100644 --- a/src/components/sidebar.vue +++ b/src/components/sidebar.vue @@ -31,13 +31,13 @@ <template v-for="n in Object.keys(nav_list)" :key="n"> <li class="nav-item w-100"> <router-link - :to="{ path: nav_list[n] }" + :to="{ name: nav_list[n] }" v-if="n == this.current_page" class="nav-link text-blue pl-4 selected-nav" >{{ n }}</router-link > <router-link - :to="{ path: nav_list[n] }" + :to="{ name: nav_list[n] }" v-else class="nav-link text-blue pl-4" >{{ n }}</router-link @@ -60,9 +60,11 @@ export default { data() { return { nav_list: { - Karyawan: "/user", - Admin: "/admin", - Departemen: "/departemen", + Karyawan: "User", + Admin: "Admin", + Departemen: "Departemen", + KPI: "ManagerkpiIndex", + Bawahan: "ManagerkaryawanIndex" }, isActive: true, }; diff --git a/src/components/table-2.vue b/src/components/table-2.vue new file mode 100644 index 0000000000000000000000000000000000000000..10cc597e831863a53d8c3c945670b1fd38223c16 --- /dev/null +++ b/src/components/table-2.vue @@ -0,0 +1,214 @@ +<template> + <div class="table-responsive"> + <table class="table table-bordered"> + <thead> + <tr> + <template v-for="c in columns" :key="c"> + <th + v-if="!c.hidden" + @click="sort(c.data)" + class="text-blue clickable" + > + {{ c.name}} + </th> + </template> + <th v-if="this.edit || this.delete" class="text-blue">Aksi</th> + </tr> + </thead> + <tbody> + + <tr + v-for="e in this.entries.slice( + (this.page - 1) * this.entry_per_page, + this.page * this.entry_per_page + )" + :key="e.id" + > + <template v-for="c in columns" :key="c"> + <template v-if="!c.hidden"> + <td v-if="c.clickable" @click="this.$emit('detail-entry', e[columns.Id.data])" class="clickable"> + {{ e[c.data] }} + </td> + <td v-else > + {{ e[c.data]}} + </td> + </template> + </template> + + <td v-if="this.edit || this.delete" style="text-align: center"> + <button v-if="this.edit" @click="this.$emit('edit-entry', e[columns.Id.data])" class="btn btn-blue"> <i class="icon ion-android-create" style="font-size: 20px"></i> Edit </button> + <button v-if="this.delete" @click="this.$emit('delete-entry', e[columns.Id.data])" class="btn-red"> <i class="icon ion-ios-trash-outline" style="font-size: 23px"></i> </button> + </td> + </tr> + </tbody> + </table> + <nav aria-label="Page navigation"> + <ul class="pagination justify-content-end"> + <li v-if="this.page > 1" class="page-item"> + <div class="page-link" @click="prevPage()">Previous</div> + </li> + <li v-else class="page-item disabled"> + <div class="page-link">Previous</div> + </li> + <template v-for="p in this.pagination" :key="p"> + <li v-if="this.page == p" class="page-item disabled"> + <div class="page-link">{{ p }}</div> + </li> + <li v-else class="page-item"> + <div class="page-link" @click="goPage(p)">{{ p }}</div> + </li> + </template> + <li v-if="this.page < this.page_num" class="page-item"> + <div class="page-link" @click="nextPage()">Next</div> + </li> + <li v-else class="page-item disabled"> + <div class="page-link">Next</div> + </li> + </ul> + </nav> + </div> +</template> + +<script> + +import { HTTP } from '../http-common' +// import axios from 'axios'; + +export default { + name: "Table2", + props: { + //map nama kolom ke data + columns: Object, + endpoint: String, + filter: String, + filter_column: String, + default_sort: { + type: String, + default: "" + }, + edit: { + type: Boolean, + default: true + }, + delete: { + type: Boolean, + default: true + }, + key: Number, + }, + data() { + return { + pagination: [], + sorted: { + column: '', + asc: true, + }, + entries: [], + entry_per_page: 10, + page: 1, + page_num: 0, + }; + }, + mounted() { + if (this.default_sort != ""){ + this.sorted.column = this.default_sort + }else{ + this.sorted.column = this.columns.Id.data + } + this.getData() + }, + watch: { + filter() { + this.page = 1 + this.getData() + } + }, + methods: { + getData(){ + let sortCol = this.sorted.column + if (!this.sorted.asc){ + sortCol = `-${sortCol}` + } + HTTP.get(`${this.endpoint}?page=${this.page}&sort=${sortCol}&filter[${this.filter_column}]=${this.filter}`).then((res)=>{ + if (res.status == 200){ + console.log(res.data) + this.entries = res.data.data.data + this.page = res.data.data.current_page + this.entry_per_page = res.data.data.per_page + this.page_num = res.data.data.last_page + this.updatePagination(); + } + }) + }, + sort(column) { + console.log("sort "+ column) + if (column === this.sorted.column) { + this.sorted.asc = !this.sorted.asc; + } else { + this.sorted.column = column; + this.sorted.asc = true + } + this.page = 1 + this.getData() + }, + nextPage() { + if (this.page < this.page_num) { + this.page += 1; + this.getData() + } + }, + prevPage() { + if (this.page > 1) { + this.page -= 1; + this.getData() + } + }, + goPage(i) { + this.page = i; + this.getData() + }, + updatePagination() { + this.pagination = []; + for (let i = this.page - 2; i <= this.page + 2; i++) { + if (i <= this.page_num && i > 0) { + this.pagination.push(i); + } + } + }, + }, +}; +</script> + +<style scoped> +li { + padding: 0; +} + +.table-bordered td, +.table-bordered th, +.table-bordered thead { + border: 1px solid #6992b4 !important; +} + +.btn-blue { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 5px; + margin-right: 5px; + vertical-align:middle +} + +.btn-red { + background-color: #f4476b; + color: #f1f7fc; + border-radius: 8px; + padding-left: 5px; + padding-right: 5px; + vertical-align:middle +} + +th{ + text-align: center; +} +</style> diff --git a/src/components/table.vue b/src/components/table.vue index abe789813d60b703b1cee9d72e6180b63541039f..bd7c946e3f70d5a06fd3d2e7f7f0b40b248f8e28 100644 --- a/src/components/table.vue +++ b/src/components/table.vue @@ -3,18 +3,20 @@ <table class="table table-bordered"> <thead> <tr> + <template v-for="c in columns" :key="c"> <th - v-for="c in Object.keys(columns)" - :key="c" + v-if="!c.hidden" @click="sort(c)" class="text-blue clickable" > - {{ c }} + {{ c.name}} </th> - <th class="text-blue">Aksi</th> + </template> + <th v-if="this.edit || this.delete" class="text-blue">Aksi</th> </tr> </thead> <tbody> + <tr v-for="e in this.entries.slice( (this.page - 1) * this.entry_per_page, @@ -23,23 +25,25 @@ :key="e.id" > <template v-for="c in columns" :key="c"> - <td v-if="c == Object.values(columns)[0]" @click="this.$emit('detail-entry', e.id)" class="clickable"> - {{ e[c] }} + <template v-if="!c.hidden"> + <td v-if="c.clickable" @click="this.$emit('detail-entry', e[columns.Id.data])" class="clickable"> + {{ e[c.data] }} </td> <td v-else > - {{ e[c] }} + {{ e[c.data]}} </td> + </template> </template> <td style="text-align: center"> - <button @click="this.$emit('edit-entry', e.id)" class="btn btn-blue"> <i class="icon ion-android-create" style="font-size: 20px"></i> Edit </button> - <button @click="this.$emit('delete-entry', e.id)" class="btn-red"> <i class="icon ion-ios-trash-outline" style="font-size: 23px"></i> </button> + <button v-if="this.edit" @click="this.$emit('edit-entry', e[columns.Id.data])" class="btn btn-blue"> <i class="icon ion-android-create" style="font-size: 20px"></i> Edit </button> + <button v-if="this.delete" @click="this.$emit('delete-entry', e[columns.Id.data])" class="btn-red"> <i class="icon ion-ios-trash-outline" style="font-size: 23px"></i> </button> </td> </tr> </tbody> </table> <nav aria-label="Page navigation"> - <ul class="pagination justify-content-end"> + <ul class="pagination justify-content-end clickable"> <li v-if="this.page > 1" class="page-item"> <div class="page-link" @click="prevPage()">Previous</div> </li> @@ -73,16 +77,23 @@ export default { //map nama kolom ke data columns: Object, filter: String, + edit: { + type: Boolean, + default: true + }, + delete: { + type: Boolean, + default: true + }, }, data() { return { - // entries: [], pagination: [], sorted: { column: "", asc: true, }, - entry_per_page: 2, + entry_per_page: 5, page: 1, page_num: 0, }; @@ -97,7 +108,7 @@ export default { } this.changes(entries); - + return entries; }, }, @@ -160,7 +171,7 @@ export default { /* eslint-disable no-unused-vars */ filterEntry(entry) { for (const [key, value] of Object.entries(this.columns)) { - if (entry[value].toLowerCase().includes(this.filter)) { + if (entry[value.data].toLowerCase().includes(this.filter)) { return true; } } diff --git a/src/router/index.js b/src/router/index.js index 909f8e9507390239a2d592f9655273c007a3317e..2ccb3a02e48a9b1ce39f261ed5a8737d51550659 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -11,12 +11,21 @@ import DepartemenIndex from "../views/DepartemenIndex.vue"; import KaryawanEdit from "../views/KaryawanEdit.vue"; import AdminInsert from "../views/AdminInsert.vue"; import AdminEdit from "../views/AdminEdit.vue"; +import KpiIndex from "../views/KpiIndex.vue"; +import KpiCreate from "../views/KpiCreate.vue"; +import KpiManagerDetail from "../views/KpiManagerDetail.vue"; +import Test from "../views/Test.vue"; +import ManagerKpiIndex from "../views/ManagerKpiIndex.vue"; +import ManagerKaryawanIndex from "../views/ManagerKaryawanIndex.vue"; +import ManagerKaryawanDetail from "../views/ManagerKaryawanDetail.vue"; +import ResponseIndex from "../views/ResponseIndex.vue"; +import ResponseDetail from "../views/ResponseDetail.vue"; const routes = [ { path: "/", name: "Home", - component: Home, + component: Login, }, { path: "/login", @@ -73,6 +82,51 @@ const routes = [ name: "AdminEdit", component: AdminEdit, }, + { + path: "/kpi", + name: "kpiIndex", + component: KpiIndex, + }, + { + path: "/manager/kpi/create", + name: "KpiCreate", + component: KpiCreate, + }, + { + path: "/manager/kpi/:id", + name: "KpiManagerDetail", + component: KpiManagerDetail, + }, + { + path: "/manager/kpi", + name: "ManagerkpiIndex", + component: ManagerKpiIndex, + }, + { + path: "/manager/employees", + name: "ManagerkaryawanIndex", + component: ManagerKaryawanIndex, + }, + { + path: "/manager/employees/:id", + name: "ManagerKaryawanDetail", + component: ManagerKaryawanDetail, + }, + { + path: "/manager/kpi/:kpiId/employees/:empId/responses", + name: "ResponseIndex", + component: ResponseIndex + }, + { + path: "/manager/kpi/:kpiId/employees/:empId/responses/:responseId", + name: "ResponseDetail", + component: ResponseDetail + }, + { + path: "/test", + name: "test", + component: Test, + }, ]; const router = createRouter({ diff --git a/src/views/AdminEdit.vue b/src/views/AdminEdit.vue index fe0435402a0f8126b2cb2f5d863d28621ae89a20..4a8cac408719d21901016d95838b949503eccafa 100644 --- a/src/views/AdminEdit.vue +++ b/src/views/AdminEdit.vue @@ -16,32 +16,30 @@ <h1 class="text-blue">Data Admin</h1> <hr class="my-4 " style="border: 1px solid #6992b4"> </div> - <form @submit="insert" autocomplete="off" id="formAdmin"> + <form @submit="save" autocomplete="off" id="formAdmin"> <div class="row p-3"> <div class="col"> - <div class="mb-3 me-3"> - <label for="namaInput" class="text-blue"> Nama </label> - <input v-model="nama" type="text" id="namaInput" class="form-control" required> - </div> <div class="mb-3 me-3"> - <label for="usernameInput" class="text-blue"> Username </label> - <input v-model="username" type="text" id="usernameInput" class="form-control" required> - </div> - <button type="submit" class="btn-blue p-1" >Simpan</button> - <router-link :to="{ path: '/user' }" style="text-decoration: none"> - <button class="btn-red p-1">Batalkan</button> - </router-link> + <label for="usernameInput" class="text-blue"> Username </label> + <input v-model="username" type="text" id="usernameInput" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label for="passwordInput" class="text-blue"> Kata Sandi </label> + <input v-model="password" type="password" id="passwordInput" class="form-control"> + </div> + <button type="submit" class="btn-blue p-1" >Simpan</button> + <router-link :to="{ path: '/admin' }" style="text-decoration: none"> + <button class="btn-red p-1">Batalkan</button> + </router-link> + </div> <div class="col"> - <div class="mb-3"> <label for="emailInput" class="text-blue"> Email </label> <input v-model="email" type="email" id="emailInput" class="form-control" required> </div> - <div class="mb-3"> - <label for="passwordInput" class="text-blue"> Kata Sandi </label> - <input v-model="password" type="password" id="passwordInput" class="form-control"> - </div> + + </div> </div> </form> @@ -54,6 +52,7 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" +import { HTTP } from '../http-common' export default { @@ -64,76 +63,43 @@ export default { }, data() { return{ - nama: "", username: "", email: "", password: "", - temp_admins: [ - { - id: 1, - nama: "Budi", - username: "budi", - email: "budi@example.com", - tanggal: "02-15-2020", - }, - { - id: 2, - nama: "Asep", - username: "asep", - email: "asep@example.com", - tanggal: "02-12-2020", - }, - { - id: 3, - nama: "Yana", - username: "yana", - email: "yana@example.com", - tanggal: "02-10-2020", - }, - { - id: 4, - nama: "Yusuf", - username: "yusuf", - email: "yusuf@example.com", - tanggal: "02-10-2020", - }, - { - id: 5, - nama: "Siti", - username: "siti", - email: "siti@example.com", - tanggal: "02-10-2020", - }, - { - id: 6, - nama: "Yuni", - username: "yuni", - email: "yuni@example.com", - tanggal: "02-10-2020", - }, - { - id: 7, - nama: "Hadi", - username: "hadi", - email: "hadi@example.com", - tanggal: "02-10-2020", - }, - ], } }, methods: { save(e){ e.preventDefault(); - let formValue = [this.nama, this.username, this.email, this.password] - console.log(formValue) - alert("Admin berhasil ditambahkan") + let data = { + username: this.username, + email: this.email, + password: null + } + if (this.password != ""){ + data.password = this.password + } + HTTP.put(`admins/${this.id}`, data, { + headers: { + 'Content-Type': 'application/json' + } + }).then((res)=>{ + if (res.status == 200){ + alert("Admin berhasil diedit") + } + }).catch(() => alert("Gagal mengedit Admin")) + }, getData(){ // di sini ambil data user dari backend - let admin = this.temp_admins[parseInt(this.id)-1] - this.nama = admin.nama - this.username = admin.username - this.email = admin.email + HTTP.get(`admins/${this.id}`).then((res)=>{ + if (res.status == 200){ + this.username = res.data.data.username + this.email = res.data.data.email + } + } + ) + } }, diff --git a/src/views/AdminIndex.vue b/src/views/AdminIndex.vue index a632876d73fbbb6ca1344d0699eda7c0078c96cd..ac9255ebacfbb159905b40f37727fb5f209f0f30 100644 --- a/src/views/AdminIndex.vue +++ b/src/views/AdminIndex.vue @@ -55,6 +55,7 @@ import Table from "../components/table"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; +import { HTTP } from "../http-common"; export default { name: "AdminIndex", @@ -118,10 +119,31 @@ export default { }, ], columns: { - Nama: "nama", - Username: "username", - Email: "email", - Tanggal: "tanggal", + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Username: { + name : "Username", + data : "username", + hidden : false, + clickable : true + }, + Email: { + name : "Email", + data : "email", + hidden : false, + clickable : false + }, + Tanggal: { + name : "Tanggal", + data : "created_at", + hidden : false, + clickable : false + }, + }, filter: "", }; @@ -129,13 +151,27 @@ export default { methods: { getAdmins() { // di sini ambil data admin dari database - this.admins = this.temp_admins; + HTTP.get("admins").then((res)=>{ + if (res.data.success){ + this.admins = res.data.data + } + }) }, detailAdmin(id) { console.log(id); }, deleteAdmin(id) { - console.log(id); + if (confirm("Yakin akan menghapus Admin?")){ + HTTP.delete(`admins/${id}`).then((res) =>{ + if (res.status == 200){ + alert("Berhasil menghapus Admin") + this.getAdmins() + } + }).catch( () => { + alert("Gagal Menghapus Admin") + } + ) + } }, editAdmin(id) { this.$router.push({ name: "AdminEdit", params: { id: id }}); diff --git a/src/views/AdminInsert.vue b/src/views/AdminInsert.vue index bf2ec489fec7614a75e3db76df99cda41f4a3376..43b9bf95310f474f876b689db6c9ec8801e2d3f7 100644 --- a/src/views/AdminInsert.vue +++ b/src/views/AdminInsert.vue @@ -19,13 +19,13 @@ <form @submit="insert" autocomplete="off" id="formAdmin"> <div class="row p-3"> <div class="col"> - <div class="mb-3 me-3"> - <label for="namaInput" class="text-blue"> Nama </label> - <input v-model="nama" type="text" id="namaInput" class="form-control" required> - </div> <div class="mb-3 me-3"> <label for="usernameInput" class="text-blue"> Username </label> <input v-model="username" type="text" id="usernameInput" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label for="passwordInput" class="text-blue"> Kata Sandi </label> + <input v-model="password" type="password" id="passwordInput" class="form-control" required> </div> <button type="submit" class="btn-blue p-1" >Simpan</button> <router-link :to="{ path: '/user' }" style="text-decoration: none"> @@ -38,10 +38,7 @@ <label for="emailInput" class="text-blue"> Email </label> <input v-model="email" type="email" id="emailInput" class="form-control" required> </div> - <div class="mb-3"> - <label for="passwordInput" class="text-blue"> Kata Sandi </label> - <input v-model="password" type="password" id="passwordInput" class="form-control" required> - </div> + </div> </div> </form> @@ -54,6 +51,7 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" +import { HTTP } from '../http-common' export default { @@ -73,10 +71,24 @@ export default { methods: { insert(e){ e.preventDefault(); - let formValue = [this.nama, this.username, this.email, this.password] - console.log(formValue) - alert("Admin berhasil ditambahkan") - document.getElementById("formAdmin").reset(); + let data = { + username: this.username, + email: this.email, + password: this.password, + password_confirmation: this.password} + HTTP.post("register", data, { + headers:{ + 'Content-Type': 'application/json' + } + }).then((res)=>{ + if (res.status == 200){ + alert("Admin berhasil ditambahkan") + document.getElementById("formAdmin").reset(); + } + }).catch(()=>{ + alert("Gagal menambahkan Admin") + }) + } }, diff --git a/src/views/DepartemenIndex.vue b/src/views/DepartemenIndex.vue index b08c10d829a81bae918acc0f16f6b34b194f4087..e8702249c5d61c12165c888daa5c1084f3ad484a 100644 --- a/src/views/DepartemenIndex.vue +++ b/src/views/DepartemenIndex.vue @@ -123,8 +123,24 @@ export default { }, ], columns: { - No: "id", - Nama: "nama", + Id : { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + No : { + name : "No", + data : "id", + hidden : false, + clickable : false + }, + Nama : { + name : "Nama", + data : "name", + hidden : false, + clickable : true + }, }, filter: "", }; @@ -158,7 +174,10 @@ export default { // }, getDept() { // di sini ambil data admin dari database - this.dept = this.temp_dept; + HTTP.get("departments").then((res)=>{ + if (res.data.success == true){ + this.dept = res.data.data + }}) }, detailDept(id) { console.log(id); diff --git a/src/views/KaryawanEdit.vue b/src/views/KaryawanEdit.vue index a2ae3b302451447966e49014c03b08567b72889d..66e7a60c5b99c86c916d30b99decbf13135ae792 100644 --- a/src/views/KaryawanEdit.vue +++ b/src/views/KaryawanEdit.vue @@ -21,31 +21,32 @@ <div class="col"> <div class="mb-3 me-3"> <label for="namaInput" class="text-blue"> Nama </label> - <input v-model="nama" type="text" id="namaInput" class="form-control" required> + <input v-model="this.employee.name" type="text" id="namaInput" class="form-control" required> </div> <div class="mb-3 me-3"> <label for="nikInput" class="text-blue"> NIK </label> - <input v-model="nik" type="text" id="nikInput" class="form-control" required> + <input v-model="employee.nik" type="text" id="nikInput" class="form-control" required> </div> <div class="mb-3 me-3"> <label for="deptInput" class="text-blue"> Departemen </label> - <input v-model="dept_in" list="dept" id="deptInput" class="form-control" @input="getAtasan" required> + <input v-model="employee.department" list="dept" id="deptInput" class="form-control" @change="getAtasan" required> <datalist id="dept"> - <option v-for="d in this.dept" :key="d" :value="d"></option> + <option v-for="d in this.dept" :key="d" :value="d.name"></option> </datalist> </div> <div class="mb-3 me-3"> <label for="jabatanInput" class="text-blue"> Jabatan </label> - <input v-model="jbt" list="jabatan" id="jabatanInput" class="form-control" required> + <input v-model="employee.position" id="jabatanInput" class="form-control" required> + <!-- <input v-model="jbt" list="jabatan" id="jabatanInput" class="form-control" required> <datalist id="jabatan"> <option v-for="j in this.jabatan" :key="j" :value="j"></option> - </datalist> + </datalist> --> </div> <div class="mb-3 me-3"> <label for="atasanInput" class="text-blue"> Atasan </label> - <input v-model="atasan_in" list="atasan" id="atasanInput" class="form-control"> + <input v-model="employee.manager" list="atasan" id="atasanInput" class="form-control"> <datalist id="atasan"> - <option v-for="a in this.atasan" :key="a" :value="a"></option> + <option v-for="a in this.atasan" :key="a.id" :value="a.name"></option> </datalist> </div> <button type="submit" class="btn-blue p-1" >Simpan</button> @@ -56,11 +57,11 @@ <div class="col"> <div class="mb-3"> <label for="usernameInput" class="text-blue"> Username </label> - <input v-model="username" type="text" id="usernameInput" class="form-control" required> + <input v-model="employee.user.username" type="text" id="usernameInput" class="form-control" required> </div> <div class="mb-3"> <label for="emailInput" class="text-blue"> Email </label> - <input v-model="email" type="email" id="emailInput" class="form-control" required> + <input v-model="employee.user.email" type="email" id="emailInput" class="form-control" required> </div> <div class="mb-3"> <label for="passwordInput" class="text-blue"> Kata Sandi </label> @@ -78,6 +79,7 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" +import { HTTP } from "../http-common"; export default { @@ -99,7 +101,28 @@ export default { username: "", email: "", password: "", + emp_id:-1, id: -1, + deptId: null, + atasanId: -1, + employee: { + user_id: "", + manager_id: "", + department_id: "", + manager: "", + department: "", + nik: "", + name: "", + slug: "", + position: "", + email: "", + username: "", + password:"", + user : { + email: "", + username: "" + } + }, temp_dept: ["Keuangan", "Marketing", "Operasional"], temp_atasan: ["Yusuf", "Budi"], temp_jabatan: ["Manager", "Staf"], @@ -165,40 +188,117 @@ export default { }, methods: { getDept(){ - this.dept = this.temp_dept + // this.dept = this.temp_dept + HTTP.get("departments").then((res)=>{ + if (res.data.success == true){ + console.log(res.data.data) + this.dept = res.data.data + }}) }, getJabatan(){ this.jabatan = this.temp_jabatan }, getAtasan(){ - this.atasan = this.temp_atasan + this.dept.forEach((d) => { + if (d.name == this.employee.department){ + this.deptId = d.id + this.employee.department_id = d.id + HTTP.get(`departments/${this.deptId}/employees?except=${this.emp_id}`).then((res)=>{ + if (res.data.success == true){ + this.atasan = [] + res.data.data.forEach((a) => { + this.atasan.push({name: a.name, id: a.id}) + }) + } + }) + } + }) }, save(e){ e.preventDefault(); + + //cek departemen + if (this.deptId == null){ + alert("Departemen tidak ditemukan") + return + } + + //cari id atasan + let atasan_id = null + this.atasan.forEach(a => { + if (a.name == this.employee.manager){ + atasan_id = a.id + this.employee.manager_id = a.id + } + }); + if (atasan_id == null){ + alert(`Atasan tidak ditemukan dalam departemen ${this.dept_in}`) + return + } + // let formValue = [this.nama, this.nik, this.deptId, this.jbt, atasan_id, this.username, this.email, this.password] + + + let data = { + user_id : this.employee.user_id, + manager_id : this.employee.manager_id, + department_id : this.employee.department_id, + nik : this.employee.nik, + name : this.employee.name, + position : this.employee.position, + email : this.employee.user.email, + username : this.employee.user.username, + password : this.password, + slug : this.employee.slug + } + + HTTP.put(`employees/${this.id}`, data, { + headers: { + 'Content-Type': 'application/json' + } + }).then((res)=>{ + console.log(res) + alert("Data Karyawan berhasil diedit") + }).catch(error => { + console.log("ERR:: ",error.response.data); + + }); + + + console.log(data) + //di sini simpen data ke backend - let formValue = [this.nama, this.nik, this.dept_in, this.jbt, this.atasan_in, this.username, this.email, this.password] - console.log(formValue) - alert("Data Karyawan berhasil diedit") + }, getData(){ // di sini ambil data user dari backend - let user = this.temp_users[parseInt(this.id)-1] - this.nama = user.nama - this.nik = "123" - this.dept_in = user.departemen - this.jbt = user.jabatan - this.atasan_in = "Budi" - this.username = user.nama - this.email = user.email + HTTP.get(`employees/${this.id}`).then((res)=>{ + if (res.data.success){ + this.employee = res.data.data + console.log(res.data.data) + let user = res.data.data + this.nama = user.name + this.nik = user.nik + this.dept_in = user.department + this.deptId = user.dept_id + this.jbt = user.position + this.atasan_id = user.manager_id + this.username = user.user.username + this.email = user.user.email + this.emp_id = user.id + this.getAtasan() + } + }) + } }, mounted(){ this.getDept() - this.getJabatan() + // this.getJabatan() this.id = this.$route.params.id this.getData() + } } diff --git a/src/views/KaryawanInsert.vue b/src/views/KaryawanInsert.vue index 8abf689bc80cd9607629cbf6dfc5afb47351f399..d80f6f7ac422543c94dd443b1807d6934c7fdf52 100644 --- a/src/views/KaryawanInsert.vue +++ b/src/views/KaryawanInsert.vue @@ -31,21 +31,22 @@ <label for="deptInput" class="text-blue"> Departemen </label> <input v-model="dept_in" list="dept" id="deptInput" class="form-control" @input="getAtasan" required> <datalist id="dept"> - <option v-for="d in this.dept" :key="d" :value="d"></option> + <option v-for="d in this.dept" :key="d" :value="d.name"></option> </datalist> </div> <div class="mb-3 me-3"> <label for="jabatanInput" class="text-blue"> Jabatan </label> - <input v-model="jbt" list="jabatan" id="jabatanInput" class="form-control" required> + <input v-model="jbt" id="jabatanInput" class="form-control" required> + <!-- <input v-model="jbt" list="jabatan" id="jabatanInput" class="form-control" required> <datalist id="jabatan"> <option v-for="j in this.jabatan" :key="j" :value="j"></option> - </datalist> + </datalist> --> </div> <div class="mb-3 me-3"> <label for="atasanInput" class="text-blue"> Atasan </label> <input v-model="atasan_in" list="atasan" id="atasanInput" class="form-control"> <datalist id="atasan"> - <option v-for="a in this.atasan" :key="a" :value="a"></option> + <option v-for="a in this.atasan" :key="a" :value="a.name"></option> </datalist> </div> <button type="submit" class="btn-blue p-1" >Simpan</button> @@ -78,6 +79,7 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" +import { HTTP } from "../http-common"; export default { @@ -99,6 +101,7 @@ export default { username: "", email: "", password: "", + deptId: null, temp_dept: ["Keuangan", "Marketing", "Operasional"], temp_atasan: ["Yusuf", "Budi"], temp_jabatan: ["Manager", "Staf"], @@ -106,20 +109,81 @@ export default { }, methods: { getDept(){ - this.dept = this.temp_dept + HTTP.get("departments").then((res)=>{ + if (res.data.success == true){ + console.log(res.data.data) + this.dept = res.data.data + }}) }, getJabatan(){ this.jabatan = this.temp_jabatan }, getAtasan(){ - this.atasan = this.temp_atasan + this.dept.forEach((d) => { + if (d.name == this.dept_in){ + this.deptId = d.id + // this.employee.department_id = d.id + HTTP.get(`departments/${this.deptId}/employees`).then((res)=>{ + if (res.data.success == true){ + this.atasan = [] + res.data.data.forEach((a) => { + this.atasan.push({name: a.name, id: a.id}) + }) + } + }) + } + }) }, insert(e){ e.preventDefault(); - let formValue = [this.nama, this.nik, this.dept_in, this.jbt, this.atasan_in, this.username, this.email, this.password] - console.log(formValue) - alert("Karyawan berhasil ditambahkan") - document.getElementById("formKaryawan").reset(); + + //cek departemen + if (this.deptId == null){ + alert("Departemen tidak ditemukan") + return + } + + //cari id atasan + let atasan_id = null + this.atasan.forEach(a => { + if (a.name == this.atasan_in){ + atasan_id = a.id + } + }); + if (atasan_id == null){ + alert(`Atasan tidak ditemukan dalam departemen ${this.dept_in}`) + return + } + // let formValue = [this.nama, this.nik, this.deptId, this.jbt, atasan_id, this.username, this.email, this.password] + + + let data = { + manager_id : atasan_id, + department_id : this.deptId, + nik : this.nik, + name : this.nama, + position : this.jbt, + email : this.email, + username : this.username, + password : this.password, + password_confirmation : this.password + } + + HTTP.post(`register-employee`, data, { + headers: { + 'Content-Type': 'application/json' + } + }).then(()=>{ + // console.log(res) + alert("Karyawan berhasil ditambahkan") + document.getElementById("formKaryawan").reset(); + }).catch(error => { + console.log("ERR:: ",error.response.data); + + }); + + + } }, diff --git a/src/views/KpiCreate.vue b/src/views/KpiCreate.vue new file mode 100644 index 0000000000000000000000000000000000000000..5f251192772e5190a9ecf8b04ee95a75453a97bd --- /dev/null +++ b/src/views/KpiCreate.vue @@ -0,0 +1,192 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-3"> + <router-link :to="{ path: '/manager/kpi' }" style="text-decoration: none"> + <div class="d-flex flex-row justify-content-start align-items-center illustration" id="back"> + <i class="icon ion-chevron-left text-blue" style="font-size: 20px"></i> + <p class="text-blue ms-1">kembali</p> + </div> + </router-link> + </div> + <div class="row p-3"> + <h1 class="text-blue">Buat KPI</h1> + <hr class="my-4 " style="border: 1px solid #6992b4"> + </div> + <form @submit="insert" autocomplete="off" id="formKPI"> + <div class="row p-3"> + <div class="col"> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Nama </label> + <input v-model="kpi_name" type="text" id="NameInput" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label for="deskripsiInput" class="text-blue"> Deskripsi </label> + <input v-model="description" type="text" id="deskripsiInput" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label for="typeInput" class="text-blue"> Periode </label> + <select + v-model="period_type" + class="form-select" + aria-label="Default select example" + id="typeInput"> + <option value="daily" selected>Harian</option> + <option value="weekly">Mingguan</option> + <option value="monthly">Bulanan</option> + <option value="custom">Custom</option> + </select> + </div> + <div v-if="period_type == 'custom'" class="mb-3 me-3"> + <label for="deadlineInput" class="text-blue"> Waktu Deadline </label> + <input v-model="deadline" type="date" id="deadlineInput" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label for="deadlineTimeInput" class="text-blue"> Waktu Deadline </label> + <input v-model="deadline_time" type="time" id="deadlineTimeInput" class="form-control" required> + </div> + + <button type="submit" class="btn-blue p-1" >Simpan</button> + <router-link :to="{ path: '/user' }" style="text-decoration: none"> + <button class="btn-red p-1">Batalkan</button> + </router-link> + </div> + <div class="col"> + <div class="mb-3"> + <label for="typeInput" class="text-blue"> Jenis Target </label> + <select + v-model="target_type" + class="form-select" + aria-label="Default select example" + id="typeInput"> + <option value="number" selected>Angka</option> + <option value="percentage">Persentase</option> + </select> + </div> + <div class="mb-3"> + <label for="targetInput" class="text-blue"> Target </label> + <input v-model="target" type="number" id="tarrgetInput" class="form-control" required> + </div> + <div class="mb-3"> + <label for="weightInput" class="text-blue"> Bobot </label> + <input v-model="weight" type="number" id="weightInput" class="form-control" required> + </div> + <div class="mb-3"> + <input v-model="inverted" class="form-check-input mt-1" type="checkbox" value="" id="CheckDefault" > + <label class="form-check-label text-blue ms-2" for="CheckDefault">Target Inverted </label> + </div> + + </div> + </div> + </form> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import { HTTP } from '../http-common' + + +export default { + name: "KpiCreate", + components: { + Header, + Sidebar + }, + data() { + return{ + emp_id: 1, + kpi_name: "", + description: "", + target: "", + inverted: false, + target_type: "number", + weight: 0, + period_type: "Daily", + deadline: null, + deadline_time: "", + } + }, + methods: { + insert(e){ + e.preventDefault(); + if (this.inverted){ + this.inverted = 1 + }else{ + this.inverted = 0 + } + + if (this.period_type != "custom"){ + this.deadline = null + } + + let data = { + name: this.kpi_name, + description: this.description, + target: this.target, + is_score_inverted: this.inverted, + target_type: this.target_type, + weight: this.weight, + period_type: this.period_type, + deadline: this.deadline, + deadline_time: this.deadline_time} + console.log(data) + HTTP.post("kpis", data, { + headers:{ + 'Content-Type': 'application/json' + } + }).then((res)=>{ + if (res.status == 200){ + alert("KPI berhasil dibuat") + document.getElementById("formKPI").reset(); + } + }).catch((err)=>{ + alert(err) + }) + + } + + }, + mounted(){ + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +#back{ + cursor: pointer; +} + +.btn-blue { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 8px; + margin-right: 5px; +} + +.btn-red { + background-color: #f4476b; + color: #f1f7fc; + border-radius: 8px; +} + +</style> \ No newline at end of file diff --git a/src/views/KpiIndex.vue b/src/views/KpiIndex.vue new file mode 100644 index 0000000000000000000000000000000000000000..2449e5b8a06e11180b3bfc1b7209d274ee6c608a --- /dev/null +++ b/src/views/KpiIndex.vue @@ -0,0 +1,88 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <!-- isi mulai di sini --> + <div class="col-6 mx-3 mb-3"> + <select + v-model="filter" + class="form-select" + aria-label="Default select example" + @change="getKpi"> + <option value="Harian" selected>Harian</option> + <option value="Mingguan">Mingguan</option> + <option value="Bulanan">Bulanan</option> + <option value="Custom">Custom</option> + </select> + </div> + <template v-for="kpi in kpis" :key="kpi"> + <KpiCard :kpi="kpi"></KpiCard> + </template> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import KpiCard from "../components/kpiCard" + + +export default { + name: "KpiIndex", + components: { + Header, + Sidebar, + KpiCard + }, + data(){ + return{ + temp_kpi: { + employee_id: 1, + name: "KPI", + description: "Deskripsi dari KPI", + target: "100%", + is_score_inverted: false, + target_type: "percentage", + weight: "20", + deadline: null, + deadline_time: "23:00", + }, + filter:"Harian", + kpis: [], + + } + }, + methods: { + getKpi(){ + this.kpis = [] + this.kpis.push(this.temp_kpi) + this.kpis.push(this.temp_kpi) + this.kpis.push(this.temp_kpi) + this.kpis.push(this.temp_kpi) + } + }, + mounted(){ + this.getKpi() + } +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + + +</style> \ No newline at end of file diff --git a/src/views/KpiManagerDetail.vue b/src/views/KpiManagerDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..143ea79aa2522c4ae78c598556d62cea5efd956b --- /dev/null +++ b/src/views/KpiManagerDetail.vue @@ -0,0 +1,293 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-3"> + <router-link :to="{ path: '/manager/kpi' }" style="text-decoration: none"> + <div class="d-flex flex-row justify-content-start align-items-center illustration" id="back"> + <i class="icon ion-chevron-left text-blue" style="font-size: 20px"></i> + <p class="text-blue ms-1">kembali</p> + </div> + </router-link> + </div> + <div class="row p-3"> + <h1 class="text-blue">{{this.kpi.name}}</h1> + <hr class="my-4 " style="border: 1px solid #6992b4"> + </div> + <form @submit="insert" autocomplete="off" id="formAdmin"> + <div class="row p-3"> + <div class="col"> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Nama </label> + <input v-model="kpi.name" type="text" id="NameInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="deskripsiInput" class="text-blue"> Deskripsi </label> + <input v-model="kpi.description" type="text" id="deskripsiInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="typeInput" class="text-blue"> Periode </label> + <select + v-model="kpi.period_type" + class="form-select" + aria-label="Default select example" + id="typeInput" + disabled> + <option value="daily" selected>Harian</option> + <option value="weekly">Mingguan</option> + <option value="monthly">Bulanan</option> + <option value="custom">Custom</option> + </select> + </div> + <div v-show="period_type == 'Custom'" class="mb-3 me-3"> + <label for="deadlineInput" class="text-blue"> Waktu Deadline </label> + <input v-model="kpi.deadline" type="date" id="deadlineInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="deadlineTimeInput" class="text-blue"> Waktu Deadline </label> + <input v-model="kpi.deadline_time" type="time" id="deadlineTimeInput" class="form-control" readonly> + </div> + </div> + <div class="col"> + <div class="mb-3"> + <label for="typeInput" class="text-blue"> Jenis Target </label> + <select + v-model="kpi.target_type" + class="form-select" + aria-label="Default select example" + id="typeInput" disabled> + <option value="number" selected>Angka</option> + <option value="percentage">Persentase</option> + </select> + </div> + <div class="mb-3"> + <label for="targetInput" class="text-blue"> Target </label> + <input v-model="kpi.target" type="number" id="tarrgetInput" class="form-control" readonly> + </div> + <div class="mb-3"> + <label for="weightInput" class="text-blue"> Bobot </label> + <input v-model="kpi.weight" type="number" id="weightInput" class="form-control" readonly> + </div> + <div class="mb-3"> + <input v-model="kpi.is_score_inverted" class="form-check-input mt-1" type="checkbox" value="" id="CheckDefault" disabled> + <label class="form-check-label text-blue ms-2" for="CheckDefault">Target Inverted </label> + </div> + + </div> + </div> + </form> + <div class="row p-3"> + <h3 class="text-blue">Assignee</h3> + <hr class="my-2 " style="border: 1px solid #6992b4"> + <div class="col"> + <v-select multiple v-model="selected_employee" + :options="employees" + label="name" + :selectable="(option) => !assignees.some(assignee => assignee.id === option.id)"> + </v-select> + </div> + <div class="col"> + <button @click="assign" class="btn float-end">Assign KPI</button> + </div> + </div> + <div class="row p-3"> + <Table + :data="assignees" + :columns="columns" + :filter="filter" + :edit= false + @delete-entry="detach" + ></Table> + </div> + + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import Table from "../components/table" +import { HTTP } from '../http-common' +import vSelect from 'vue-select' +import 'vue-select/dist/vue-select.css' + + +export default { + name: "KpiManagerDetail", + components: { + Header, + Sidebar, + Table, + vSelect + }, + data() { + return{ + temp_kpi : { + employee_id: 1, + name: "Kebersihan", + description: "Lingkungan Salman Bersih", + target: 100, + is_score_inverted: false, + target_type: "number", + weight: 20, + period_type: "daily", + deadline: "", + deadline_time: "23:59", + }, + kpi : {}, + filter: "", + kpi_id: "", + columns: { + Id : { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Nama : { + name : "Nama", + data : "name", + hidden : false, + clickable : false + }, + }, + assignees: [], + temp_assignees: [ + { + id : 1, + name : "Budi" + } + ], + employees: [], + selected_employee: [], + temp_employees: [ + { + id: 1, + name: "Budi", + }, + { + id: 2, + name: "Asep", + }, + { + id: 3, + name: "Yana", + }, + { + id: 4, + name: "Yusuf", + }, + { + id: 5, + name: "Siti", + }, + { + id: 6, + name: "Yuni", + }, + { + id: 7, + name: "Hadi", + }, + ], + } + }, + methods: { + assign(){ + //for all selected_employee attach kpi + //tambahin ke employees + let emp_ids = {employee_ids: []} + this.selected_employee.forEach((emp) => { + this.assignees.push(emp) + emp_ids.employee_ids.push(emp.id) + }) + HTTP.post(`kpis/${this.kpi_id}/attach`, emp_ids).catch(()=>{ + alert("Gagal assign KPI") + return + }) + this.selected_employee = [] + }, + detach(id){ + //detach kpi ke backend + let emp_ids = {employee_ids: [id]} + HTTP.post(`kpis/${this.kpi_id}/detach`, emp_ids).catch(()=>{ + alert("Gagal menghapus KPI dari karyawan") + return + }) + this.assignees = this.assignees.filter(x => x.id != id) + }, + getBawahan(){ + //get bawahan ke backend + this.employees = this.temp_employees + }, + getAssignee(){ + this.assignees = this.temp_assignees + this.getBawahan() + //get assignee ke backend then getBawahan + }, + getKpi(){ + //get kpi dengan id kpi_id + HTTP.get(`kpis/${this.kpi_id}`).then((res)=>{ + console.log(res.data.data) + this.kpi = res.data.data + this.kpi.deadline_time = this.kpi.deadline_time.replace(".", ":") + }).catch(()=> alert("Error loading page")) + } + + }, + mounted(){ + this.kpi_id = this.$route.params.id + this.getKpi() + this.getAssignee() + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +#back{ + cursor: pointer; +} + +.btn-blue { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 8px; + margin-right: 5px; +} + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; + width: fit-content; +} + +.btn-red { + background-color: #f4476b; + color: #f1f7fc; + border-radius: 8px; +} + +.modal{ + margin: auto +} + +</style> \ No newline at end of file diff --git a/src/views/ManagerKaryawanDetail.vue b/src/views/ManagerKaryawanDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..5061b6ecd198b363ebcd83386ada989167ccb890 --- /dev/null +++ b/src/views/ManagerKaryawanDetail.vue @@ -0,0 +1,161 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-3"> + <router-link :to="{ path: '/manager/employees' }" style="text-decoration: none"> + <div class="d-flex flex-row justify-content-start align-items-center illustration" id="back"> + <i class="icon ion-chevron-left text-blue" style="font-size: 20px"></i> + <p class="text-blue ms-1">kembali</p> + </div> + </router-link> + </div> + <div class="row p-2"> + <div class="col"> + <h1 class="text-blue">NAMA</h1> + </div> + </div> + <form @submit="report" autocomplete="off" id="formReport"> + <div class="row p-2"> + + <div class="col p-2"> + <label for="startReport" class="text-blue"> Tanggal awal </label> + <input v-model="startReport" type="date" id="startReport" class="form-control" required> + </div> + <div class="col p-2"> + <label for="endReport" class="text-blue"> Tanggal akhir </label> + <input v-model="endReport" type="date" id="endReport" class="form-control" required> + </div> + <div class="col p-2"> + <button type="submit" class="btn-blue p-1 mt-3" >Buat Report</button> + </div> + </div> + </form> + <hr class="m-2"> + <div class="row p-2"> + <h2 class="text-blue">Daftar KPI</h2> + </div> + <div class="row p-2"> + <div class="col-6 p-2"> + <select + v-model="filter" + class="form-select" + aria-label="Default select example" + > + <option selected value="">Semua</option> + <option v-for="p in periods" :key="p" :value="p.data">{{p.name}}</option> + </select> + </div> + </div> + <div class="row p-2"> + <!-- Ganti endpoint --> + <Table2 + :endpoint="'kpis'" + :columns="columns" + :filter="filter" + :filter_column="'period_type'" + :edit = "false" + :delete = "false" + @detail-entry="openResponses" + ></Table2> + </div> + </div> + </div> + </div> +</template> + +<script> + +import Table2 from "../components/table-2"; +import Header from "../components/header"; +import Sidebar from "../components/sidebar"; +// import { HTTP } from "../http-common"; + + +export default { + name: "ManagerKaryawanDetail", + components: { + Header, + Sidebar, + Table2 + }, + data(){ + return{ + filter: "", + columns: { + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Nama: { + name : "Nama", + data : "name", + hidden : false, + clickable : true + }, + Periode: { + name : "Periode", + data : "period_type", + hidden : false, + clickable : false + }, + }, + periods: [ + {name: "Harian", + data: "daily"}, + {name: "Mingguan", + data: "weekly"}, + {name: "Bulanan", + data: "monthly"}, + {name: "Tahunan", + data: "yearly"}, + {name: "Custom", + data: "custom"}, + ], + id: -1, + emp: { + name : "Nama Karyawan" + }, + } + }, + mounted(){ + this.id = this.$route.params.id + }, + methods: { + openResponses(idKpi){ + this.$router.push({ name: "ResponseIndex", params: { kpiId: idKpi, empId:this.id}}); + }, + report(){ + //download report + } + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +.btn-blue { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 8px; + margin-right: 5px; +} + + + +</style> \ No newline at end of file diff --git a/src/views/ManagerKaryawanIndex.vue b/src/views/ManagerKaryawanIndex.vue new file mode 100644 index 0000000000000000000000000000000000000000..fcc8ee07cb70bb56c8782d6dfd29e2fdc7b18bda --- /dev/null +++ b/src/views/ManagerKaryawanIndex.vue @@ -0,0 +1,98 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-2"> + <div class="col"> + <h1 class="text-blue">Daftar Karyawan</h1> + </div> + </div> + <div class="row p-2"> + <Table2 + :endpoint="'kpis'" + :columns="columns" + :filter="filter" + :filter_column="'period_type'" + :edit = "false" + :delete = "false" + @detail-entry="detailKaryawan" + ></Table2> + </div> + </div> + </div> + </div> +</template> + +<script> + +import Table2 from "../components/table-2"; +import Header from "../components/header"; +import Sidebar from "../components/sidebar"; +// import { HTTP } from "../http-common"; + + +export default { + name: "ManagerKaryawanIndex", + components: { + Header, + Sidebar, + Table2 + }, + data(){ + return{ + filter: "", + columns: { + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Nama: { + name : "Nama", + data : "name", + hidden : false, + clickable : true + }, + Periode: { + name : "Periode", + data : "period_type", + hidden : false, + clickable : false + }, + } + } + }, + methods: { + detailKaryawan(id){ + //routing page detail + console.log(id) + } + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + + +</style> \ No newline at end of file diff --git a/src/views/ManagerKpiIndex.vue b/src/views/ManagerKpiIndex.vue new file mode 100644 index 0000000000000000000000000000000000000000..b444f2da0669095e2d06f81a27b97b7ed25ca8aa --- /dev/null +++ b/src/views/ManagerKpiIndex.vue @@ -0,0 +1,142 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-2"> + <div class="col"> + <h1 class="text-blue">Daftar KPI</h1> + </div> + <div class="col"> + <router-link :to="{ path: '/manager/kpi/create' }" style="text-decoration: none"> + <button class="btn float-end">Buat KPI</button> + </router-link> + </div> + </div> + <div class="row p-2"> + <div class="col-6 p-2"> + <select + v-model="filter" + class="form-select" + aria-label="Default select example" + > + <option selected value="">Semua</option> + <option v-for="p in periods" :key="p" :value="p.data">{{p.name}}</option> + </select> + </div> + </div> + <div class="row p-2"> + <Table2 + :endpoint="'kpis'" + :columns="columns" + :filter="filter" + :filter_column="'period_type'" + :key = "componentKey" + @edit-entry="editKpi" + @delete-entry="deleteKpi" + @detail-entry="detailKpi" + ></Table2> + </div> + </div> + </div> + </div> +</template> + +<script> + +import Table2 from "../components/table-2"; +import Header from "../components/header"; +import Sidebar from "../components/sidebar"; +import { HTTP } from "../http-common"; + + +export default { + name: "ManagerKpiIndex", + components: { + Header, + Sidebar, + Table2 + }, + data(){ + return{ + filter: "", + columns: { + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Nama: { + name : "Nama", + data : "name", + hidden : false, + clickable : true + }, + Periode: { + name : "Periode", + data : "period_type", + hidden : false, + clickable : false + }, + }, + periods: [ + {name: "Harian", + data: "daily"}, + {name: "Mingguan", + data: "weekly"}, + {name: "Bulanan", + data: "monthly"}, + {name: "Tahunan", + data: "yearly"}, + {name: "Custom", + data: "custom"}, + ], + componentKey : 1, + } + }, + methods: { + deleteKpi(id){ + if (confirm("Yakin akan menghapus KPI?")){ + HTTP.delete(`kpis/${id}`).then(()=>{ + this.componentKey += 1 + }).catch(()=>{ + alert("Gagal Menghapus KPI") + }) + } + }, + editKpi(id){ + //routing page edit + console.log(id) + }, + detailKpi(id){ + //routing page detail + this.$router.push({ name: "KpiManagerDetail", params: { id: id }}); + } + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + + +</style> \ No newline at end of file diff --git a/src/views/ResponseDetail.vue b/src/views/ResponseDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..588a496d535bdf3850519b5e2d1f40d18395d734 --- /dev/null +++ b/src/views/ResponseDetail.vue @@ -0,0 +1,144 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-3"> + <router-link :to="{ path: `/manager/kpi/${this.kpiId}/employees/${this.empId}/responses` }" style="text-decoration: none"> + <div class="d-flex flex-row justify-content-start align-items-center illustration" id="back"> + <i class="icon ion-chevron-left text-blue" style="font-size: 20px"></i> + <p class="text-blue ms-1">kembali</p> + </div> + </router-link> + </div> + <div class="row p-2"> + <h1 class="text-blue">{{kpi.name}}</h1> + <div class="mb-3 me-3"> + <label for="dateInput" class="text-blue"> Tanggal </label> + <input v-model="response.date" type="text" id="dateInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Target </label> + <input v-model="kpi.target" type="text" id="NameInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Aktual </label> + <input v-model="response.actual" type="text" id="NameInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Deskripsi </label> + <input v-model="response.description" type="text" id="NameInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Status </label> + <input v-model="response.status" type="text" id="NameInput" class="form-control" readonly> + </div> + <div class="col"> + <button class="btn-reject float-end mx-2">Reject</button> + <button class="btn-blue float-end mx-2">Adjust</button> + <button class="btn-green float-end mx-2">Approve</button> + </div> + </div> + <hr class="m-2"> + <div class="row p-2"> + <div class="col"> + <h1 class="text-blue">Komentar</h1> + </div> + <div class="col"> + <button class="btn-blue float-end">Buat Komentar</button> + </div> + </div> + <template v-for="c in comments" :key="c"> + <CommentCard :comment="c"></CommentCard> + </template> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import CommentCard from "../components/commentCard" + + +export default { + name: "ResponseDetail", + components: { + Header, + Sidebar, + CommentCard + }, + data(){ + return{ + temp_comment:{ + comment : "Tolong diperbaiki lagi", + created_at : "10-23-2021" + }, + comments: [], + empId: 0, + responseId: 0, + kpiId: 0, + response: { + date: "10-23-2021", + actual: 10, + description: "Deskripsi", + status: "Pending" + }, + kpi:{ + name: "Nama KPI", + target: 20 + } + + } + }, + created(){ + this.empId = this.$route.params.empId + this.kpiId = this.$route.params.kpiId + this.responseId = this.$route.params.responseId + this.getComments() + }, + methods: { + getComments(){ + this.comments.push(this.temp_comment) + this.comments.push(this.temp_comment) + } + }, +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +.btn-blue { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + +.btn-green { + background-color: #3CB371; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + +.btn-reject { + background: #f4476b; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} +</style> \ No newline at end of file diff --git a/src/views/ResponseIndex.vue b/src/views/ResponseIndex.vue new file mode 100644 index 0000000000000000000000000000000000000000..fddb71adfb4bf21094b4e462c987a5de51d3e040 --- /dev/null +++ b/src/views/ResponseIndex.vue @@ -0,0 +1,119 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <div class="row p-3"> + <router-link :to="{ path: `/manager/employees/${this.empId}` }" style="text-decoration: none"> + <div class="d-flex flex-row justify-content-start align-items-center illustration" id="back"> + <i class="icon ion-chevron-left text-blue" style="font-size: 20px"></i> + <p class="text-blue ms-1">kembali</p> + </div> + </router-link> + </div> + <div class="row p-2"> + <div class="col"> + <h1 class="text-blue">Nama KPI</h1> + </div> + </div> + <div class="row p-2"> + <Table2 + :endpoint="`kpis/${this.kpiId}/employees/${this.empId}/kpi-responses`" + :columns="columns" + :filter="filter" + :filter_column="'status'" + :edit = "false" + :delete = "false" + :default_sort = "'status'" + @detail-entry="detailResponse" + ></Table2> + </div> + </div> + </div> + </div> +</template> + +<script> + +import Table2 from "../components/table-2"; +import Header from "../components/header"; +import Sidebar from "../components/sidebar"; +// import { HTTP } from "../http-common"; + + +export default { + name: "ResponseIndex", + components: { + Header, + Sidebar, + Table2 + }, + data(){ + return{ + filter: "", + columns: { + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Tanggal: { + name : "Tanggal", + data : "date", + hidden : false, + clickable : true + }, + Nila: { + name : "Nilai", + data : "actual", + hidden : false, + clickable : false + }, + Status: { + name : "Status", + data : "status", + hidden : false, + clickable : false + }, + }, + empId:-1, + kpiId:-1, + } + }, + created(){ + this.empId = this.$route.params.empId + this.kpiId = this.$route.params.kpiId + }, + methods: { + detailResponse(id){ + //routing page detail + this.$router.push({ name: "ResponseDetail", params: { kpiId: this.kpiId, empId:this.empId, responseId:id }}); + } + } + +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + + +</style> \ No newline at end of file diff --git a/src/views/Test.vue b/src/views/Test.vue new file mode 100644 index 0000000000000000000000000000000000000000..0c051c15e566227d5a3d0f81c2c9002f42a8ac4d --- /dev/null +++ b/src/views/Test.vue @@ -0,0 +1,68 @@ +<template> + <div class="container-fluid"> + <div class="row flex-nowrap"> + <Sidebar current_page = "About"></Sidebar> + <div class="col ps-md-2 pt-2"> + <Header></Header> + <input v-model="filter" type="text"> + <Table2 :endpoint="'kpis'" :columns="columns" :filter="filter" :filter_column="'name'" @edit-entry="print" @delete-entry="print" @detail-entry="print" ></Table2> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import Table2 from "../components/table-2"; + + +export default { + name: "Template", + components: { + Header, + Sidebar, + Table2 + }, + data(){ + return{ + filter: "", + columns: { + Id: { + name : "Id", + data : "id", + hidden : true, + clickable : false + }, + Nama: { + name : "Nama", + data : "name", + hidden : false, + clickable : true + }, + }, + } + }, + methods: { + print(id){ + console.log(id) + } + } +} +</script> + +<style scoped> +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + min-height: 100vh; + background-color: #fff; +} + + +</style> \ No newline at end of file diff --git a/src/views/UserIndex.vue b/src/views/UserIndex.vue index 26eba6eaec3d9f5d683c2a739dc285d1b6928d38..12494f3ac1b6602501d362623a26d5eeafd71bbb 100644 --- a/src/views/UserIndex.vue +++ b/src/views/UserIndex.vue @@ -19,19 +19,17 @@ <div class="col p-2"> <select v-if="filter_type == 'Departemen'" - @click="filter_user" + @change="getUsers" v-model="selected_filter" class="form-select" aria-label="Default select example" > <option selected>Semua</option> - <option value="Marketing">Marketing</option> - <option value="Keuangan">Keuangan</option> - <option value="Operasional">Operasional</option> + <option v-for="d in depts" :key="d.id" :value="d.name">{{d.name}}</option> </select> <select v-else - @click="filter_user" + @change="getUsers" v-model="selected_filter" class="form-select" aria-label="Default select example" @@ -85,6 +83,7 @@ import Table from "../components/table"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; +import { HTTP } from "../http-common"; export default { name: "UserIndex", @@ -96,70 +95,44 @@ export default { data() { return { users: [], - temp_users: [ - { - id: 1, - nama: "Budi", - email: "budi@example.com", - jabatan: "manager", - departemen: "keuangan", - tanggal: "02-15-2020", - }, - { - id: 2, - nama: "Asep", - email: "asep@example.com", - jabatan: "staf", - departemen: "marketing", - tanggal: "02-12-2020", + depts: [], + columns: { + Id : { + name : "Id", + data : "slug", + hidden : true, + clickable : false }, - { - id: 3, - nama: "Yana", - email: "yana@example.com", - jabatan: "staf", - departemen: "keuangan", - tanggal: "02-10-2020", + Nama: { + name : "Nama", + data : "name", + hidden : false, + clickable : true }, - { - id: 4, - nama: "Yusuf", - email: "yusuf@example.com", - jabatan: "staf", - departemen: "keuangan", - tanggal: "02-10-2020", + Email: { + name : "Email", + data : "email", + hidden : false, + clickable : false }, - { - id: 5, - nama: "Siti", - email: "siti@example.com", - jabatan: "staf", - departemen: "keuangan", - tanggal: "02-10-2020", + Jabatan: { + name : "Jabatan", + data : "position", + hidden : false, + clickable : false }, - { - id: 6, - nama: "Yuni", - email: "yuni@example.com", - jabatan: "staf", - departemen: "keuangan", - tanggal: "02-10-2020", + Departemen: { + name : "Departemen", + data : "department", + hidden : false, + clickable : false }, - { - id: 7, - nama: "Hadi", - email: "hadi@example.com", - jabatan: "staf", - departemen: "keuangan", - tanggal: "02-10-2020", + Tanggal: { + name : "Tanggal", + data : "tanggal", + hidden : false, + clickable : false }, - ], - columns: { - Nama: "nama", - Email: "email", - Jabatan: "jabatan", - Departemen: "departemen", - Tanggal: "tanggal", }, filter: "", filter_type: "Departemen", @@ -168,14 +141,31 @@ export default { }, methods: { getUsers() { + // di sini ambil data user dari database - this.users = this.temp_users; + HTTP.get("employees").then((response) => { + if (response.data.success == true){ + this.users = response.data.data + this.users.forEach(val => { + val.email = val.user.email + val.tanggal = val.user.created_at + }) + this.filter_user() + } + }) + // this.users = this.temp_users; + }, + getDept(){ + HTTP.get("departments").then((res)=>{ + if (res.data.success == true){ + this.depts = res.data.data + }}) }, filter_user() { - this.getUsers(); if (this.selected_filter != "Semua") { + this.users = this.users.filter((entry) => - entry[this.columns[this.filter_type]] + entry[this.columns[this.filter_type].data] .toLowerCase() .includes(this.selected_filter.toLowerCase()) ); @@ -185,14 +175,26 @@ export default { console.log(id) }, deleteUser(id){ - console.log(id) + if (confirm("Yakin akan menghapus karyawan?")){ + HTTP.delete(`employees/${id}`).then((res) =>{ + if (res.status == 200){ + alert("Berhasil menghapus Karyawan") + this.getUsers() + } + }).catch( () => { + alert("Gagal Menghapus Karyawan") + } + ) + } + }, editUser(id){ this.$router.push({ name: "KaryawanEdit", params: { id: id }}); } }, mounted() { - this.getUsers(); + this.getUsers() + this.getDept() }, }; </script>