diff --git a/src/components/commentCard.vue b/src/components/commentCard.vue index adb544912912e856ee8ec491865df5a4174baf8a..8bdc3082f26b1ca9ae5b75ebc24fbb3258a0e0bd 100644 --- a/src/components/commentCard.vue +++ b/src/components/commentCard.vue @@ -5,7 +5,14 @@ </div> <div class="card-footer"> - <p class=" text-blue">{{comment.created_at}}</p> + <div class="row"> + <div class="col"> + <p class=" text-blue">{{comment.employee_name}}</p> + </div> + <div class="col"> + <p class="text-blue float-end">{{comment.created_at}}</p> + </div> + </div> </div> </div> </template> diff --git a/src/components/kpiCard.vue b/src/components/kpiCard.vue index e63b0f6bd4e0df534b8787f706b1d02d38997f1b..dc5d2d36b5feb61e69aa5907fc49351b42e8d47e 100644 --- a/src/components/kpiCard.vue +++ b/src/components/kpiCard.vue @@ -1,5 +1,5 @@ <template> - <div class="card mx-3 my-1 bground-light"> + <div class="card mx-3 my-1 bground-light clickable" @click="detail"> <div class="card-body"> <h5 class="card-title">{{kpi.name}}</h5> <p class="card-subtitle text-blue">{{kpi.description}}</p> @@ -27,7 +27,9 @@ export default { }; }, methods: { - + detail(){ + this.$router.push({ name: "KpiDetail", params: { id: this.kpi.id }}); + } }, }; </script> diff --git a/src/components/table-2.vue b/src/components/table-2.vue index 10cc597e831863a53d8c3c945670b1fd38223c16..ef636107f4ca285b207ef32ed5afe5d3c766d061 100644 --- a/src/components/table-2.vue +++ b/src/components/table-2.vue @@ -4,6 +4,17 @@ <thead> <tr> <template v-for="c in columns" :key="c"> + <template v-if="c.hasOwnProperty('child')"> + <template v-for="child in c.child" :key="child"> + <th + v-if="!child.hidden" + @click="sort(child.data)" + class="text-blue clickable" + > + {{ child.name}} + </th> + </template> + </template> <th v-if="!c.hidden" @click="sort(c.data)" @@ -18,13 +29,22 @@ <tbody> <tr - v-for="e in this.entries.slice( - (this.page - 1) * this.entry_per_page, - this.page * this.entry_per_page - )" + v-for="e in this.entries" :key="e.id" > <template v-for="c in columns" :key="c"> + <template v-if="c.hasOwnProperty('child')"> + <template v-for="child in c.child" :key="child"> + <template v-if="!child.hidden"> + <td v-if="child.clickable" @click="this.$emit('detail-entry', e[columns.Id.data])" class="clickable"> + {{ e[c.data][child.data] }} + </td> + <td v-else > + {{ e[c.data][child.data]}} + </td> + </template> + </template> + </template> <template v-if="!c.hidden"> <td v-if="c.clickable" @click="this.$emit('detail-entry', e[columns.Id.data])" class="clickable"> {{ e[c.data] }} @@ -43,7 +63,7 @@ </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> @@ -86,6 +106,10 @@ export default { type: String, default: "" }, + include: { + type: String, + default: "" + }, edit: { type: Boolean, default: true @@ -95,6 +119,7 @@ export default { default: true }, key: Number, + }, data() { return { @@ -115,12 +140,18 @@ export default { }else{ this.sorted.column = this.columns.Id.data } + if (this.endpoint != ""){ this.getData() + } + }, watch: { filter() { this.page = 1 this.getData() + }, + endpoint(){ + this.getData() } }, methods: { @@ -129,9 +160,9 @@ export default { if (!this.sorted.asc){ sortCol = `-${sortCol}` } - HTTP.get(`${this.endpoint}?page=${this.page}&sort=${sortCol}&filter[${this.filter_column}]=${this.filter}`).then((res)=>{ + HTTP.get(`${this.endpoint}?include=${this.include}&page=${this.page}&sort=${sortCol}&filter[${this.filter_column}]=${this.filter}`).then((res)=>{ if (res.status == 200){ - console.log(res.data) + // 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 @@ -145,7 +176,12 @@ export default { if (column === this.sorted.column) { this.sorted.asc = !this.sorted.asc; } else { - this.sorted.column = column; + if (column == 'department'){ + this.sorted.column = "department_id"; + }else{ + this.sorted.column = column; + } + this.sorted.asc = true } this.page = 1 diff --git a/src/components/table-response.vue b/src/components/table-response.vue new file mode 100644 index 0000000000000000000000000000000000000000..9d0b8322124fa2c89ebd9f3f250efc7e767bd122 --- /dev/null +++ b/src/components/table-response.vue @@ -0,0 +1,256 @@ +<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="isManager" 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 && c.name!='Bukti'"> + <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> + <td v-if="c.name=='Bukti'" > + <a :href="e[c.data][0].image">link</a> + </td> + </template> + + <td v-if="isManager" style="text-align: center"> + <template v-if="e.status == 'pending'"> + <button @click="this.$emit('approve', e[columns.Id.data])" class="btn-green mx-2">Approve</button> + <button @click="this.$emit('adjust', e[columns.Id.data])" class="btn-blue mx-2">Adjust</button> + <button @click="this.$emit('reject', e[columns.Id.data])" class="btn-reject mx-2">Reject</button> + </template> + </td> + </tr> + </tbody> + </table> + <nav aria-label="Page navigation"> + <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> + <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 { + components: {}, + name: "TableR", + 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, + refresh: { + type: Boolean, + default: true + }, + isManager: Boolean, + }, + 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 + } + if (this.endpoint != ""){ + this.getData() + } + + }, + watch: { + filter() { + this.page = 1 + this.getData() + }, + endpoint(){ + this.getData() + }, + refresh(){ + this.getData() + } + }, + methods: { + getData(){ + let sortCol = this.sorted.column + if (!this.sorted.asc){ + sortCol = `-${sortCol}` + } + HTTP.get(`${this.endpoint}?page=${this.page}&sort=${sortCol}`).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; +} + +.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> diff --git a/src/components/table.vue b/src/components/table.vue index bd7c946e3f70d5a06fd3d2e7f7f0b40b248f8e28..ead873c5dd54523264ac7f55baa8c2ed2fbc5e28 100644 --- a/src/components/table.vue +++ b/src/components/table.vue @@ -16,7 +16,6 @@ </tr> </thead> <tbody> - <tr v-for="e in this.entries.slice( (this.page - 1) * this.entry_per_page, @@ -34,11 +33,12 @@ </td> </template> </template> - + <template v-if="this.edit || this.delete"> <td 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> + </template> </tr> </tbody> </table> diff --git a/src/router/index.js b/src/router/index.js index 2ccb3a02e48a9b1ce39f261ed5a8737d51550659..6406aa92b3ea46b72b575dfc0cb197a428a6047b 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -9,9 +9,11 @@ import AdminIndex from "../views/AdminIndex.vue"; import KaryawanInsert from "../views/KaryawanInsert.vue"; import DepartemenIndex from "../views/DepartemenIndex.vue"; import KaryawanEdit from "../views/KaryawanEdit.vue"; +import KaryawanDetail from "../views/KaryawanDetail.vue"; import AdminInsert from "../views/AdminInsert.vue"; import AdminEdit from "../views/AdminEdit.vue"; import KpiIndex from "../views/KpiIndex.vue"; +import KpiDetail from "../views/KpiDetail.vue"; import KpiCreate from "../views/KpiCreate.vue"; import KpiManagerDetail from "../views/KpiManagerDetail.vue"; import Test from "../views/Test.vue"; @@ -20,6 +22,7 @@ import ManagerKaryawanIndex from "../views/ManagerKaryawanIndex.vue"; import ManagerKaryawanDetail from "../views/ManagerKaryawanDetail.vue"; import ResponseIndex from "../views/ResponseIndex.vue"; import ResponseDetail from "../views/ResponseDetail.vue"; +import ResponseEmployee from "../views/ResponseEmployee.vue"; const routes = [ { @@ -72,6 +75,11 @@ const routes = [ name: "KaryawanEdit", component: KaryawanEdit, }, + { + path: "/user/:id", + name: "KaryawanDetail", + component: KaryawanDetail, + }, { path: "/admin/insert", name: "AdminInsert", @@ -87,6 +95,22 @@ const routes = [ name: "kpiIndex", component: KpiIndex, }, + { + path: "/kpi/:id", + name: "KpiDetail", + component: KpiDetail, + }, + { + path: "/kpi/:kpiId/responses", + name: "KpiResponses", + component: ResponseIndex, + props: { isManager: false } + }, + { + path: "/kpi/:kpiId/responses/:responseId", + name: "ResponseEmployee", + component: ResponseEmployee, + }, { path: "/manager/kpi/create", name: "KpiCreate", @@ -115,7 +139,8 @@ const routes = [ { path: "/manager/kpi/:kpiId/employees/:empId/responses", name: "ResponseIndex", - component: ResponseIndex + component: ResponseIndex, + props: { isManager: true } }, { path: "/manager/kpi/:kpiId/employees/:empId/responses/:responseId", diff --git a/src/views/AdminIndex.vue b/src/views/AdminIndex.vue index ac9255ebacfbb159905b40f37727fb5f209f0f30..10e371870f16748f206ba9583c73c4b2408ff161 100644 --- a/src/views/AdminIndex.vue +++ b/src/views/AdminIndex.vue @@ -37,14 +37,16 @@ </div> </div> <div class="row p-2"> - <Table - :data="admins" - :columns="columns" - :filter="filter" - @delete-entry="deleteAdmin" - @edit-entry="editAdmin" - @detail-entry="detailAdmin" - ></Table> + <Table2 + :endpoint="'admins'" + :columns="columns" + :filter="filter" + :filter_column="'username'" + :key = "0" + @edit-entry="editAdmin" + @delete-entry="deleteAdmin" + @detail-entry="detailAdmin" + ></Table2> </div> </div> </div> @@ -52,7 +54,7 @@ </template> <script> -import Table from "../components/table"; +import Table2 from "../components/table-2"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; import { HTTP } from "../http-common"; @@ -60,64 +62,13 @@ import { HTTP } from "../http-common"; export default { name: "AdminIndex", components: { - Table, + Table2, Header, Sidebar, }, data() { return { admins: [], - 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", - }, - ], columns: { Id: { name : "Id", diff --git a/src/views/DepartemenIndex.vue b/src/views/DepartemenIndex.vue index 7ed4ce8dbefd1e20de517079392bbbaf59e722e5..056e3a01093a0bdb55c66237a1a382b671d331d6 100644 --- a/src/views/DepartemenIndex.vue +++ b/src/views/DepartemenIndex.vue @@ -40,14 +40,16 @@ </div> </div> <div class="row p-2"> - <Table - :data="dept" - :columns="columns" - :filter="filter" - @delete-entry="deleteDept" - @edit-entry="editDept" - @detail-entry="detailDept" - ></Table> + <Table2 + :endpoint="'departments'" + :columns="columns" + :filter="filter" + :filter_column="'name'" + :key = "0" + @edit-entry="editDept" + @delete-entry="deleteDept" + @detail-entry="detailDept" + ></Table2> </div> </div> </div> @@ -88,7 +90,7 @@ </template> <script> -import Table from "../components/table"; +import Table2 from "../components/table-2"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; import Modal from "../components/modal"; @@ -96,7 +98,7 @@ import Modal from "../components/modal"; export default { name: "DepartemenIndex", components: { - Table, + Table2, Header, Sidebar, Modal, diff --git a/src/views/KaryawanDetail.vue b/src/views/KaryawanDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..3ec1e818cc31ac704702cac11486ff3816805a99 --- /dev/null +++ b/src/views/KaryawanDetail.vue @@ -0,0 +1,339 @@ +<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: '/user' }" 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">Data Karyawan</h1> + <hr class="my-4 " style="border: 1px solid #6992b4"> + </div> + <form @submit="save" autocomplete="off" id="formKaryawan"> + <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="this.employee.name" type="text" id="namaInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="nikInput" class="text-blue"> NIK </label> + <input v-model="employee.nik" type="text" id="nikInput" class="form-control" readonly> + </div> + <div class="mb-3 me-3"> + <label for="deptInput" class="text-blue"> Departemen </label> + <input v-model="employee.department" list="dept" id="deptInput" class="form-control" readonly> + <datalist id="dept"> + <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="employee.position" id="jabatanInput" class="form-control" readonly> + <!-- <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> --> + </div> + <div class="mb-3 me-3"> + <label for="atasanInput" class="text-blue"> Atasan </label> + <input v-model="employee.manager" list="atasan" id="atasanInput" class="form-control"> + <datalist id="atasan"> + <option v-for="a in this.atasan" :key="a.id" :value="a.name"></option> + </datalist> + </div> + </div> + <div class="col"> + <div class="mb-3"> + <label for="usernameInput" class="text-blue"> Username </label> + <input v-model="employee.user.username" type="text" id="usernameInput" class="form-control" readonly> + </div> + <div class="mb-3"> + <label for="emailInput" class="text-blue"> Email </label> + <input v-model="employee.user.email" type="email" id="emailInput" class="form-control" readonly> + </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> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import { HTTP } from "../http-common"; + + +export default { + name: "KaryawanDetail", + components: { + Header, + Sidebar + }, + data() { + return{ + dept: [], + jabatan: [], + atasan: [], + nama: "", + nik: "", + dept_in: "", + jbt: "", + atasan_in: "", + 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"], + 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", + }, + { + id: 3, + nama: "Yana", + email: "yana@example.com", + jabatan: "staf", + departemen: "keuangan", + tanggal: "02-10-2020", + }, + { + id: 4, + nama: "Yusuf", + email: "yusuf@example.com", + jabatan: "staf", + departemen: "keuangan", + tanggal: "02-10-2020", + }, + { + id: 5, + nama: "Siti", + email: "siti@example.com", + jabatan: "staf", + departemen: "keuangan", + tanggal: "02-10-2020", + }, + { + id: 6, + nama: "Yuni", + email: "yuni@example.com", + jabatan: "staf", + departemen: "keuangan", + tanggal: "02-10-2020", + }, + { + id: 7, + nama: "Hadi", + email: "hadi@example.com", + jabatan: "staf", + departemen: "keuangan", + tanggal: "02-10-2020", + }, + ], + dept_last_page : 1, + } + }, + methods: { + getDept(){ + HTTP.get("departments?page=1").then((res)=>{ + if (res.data.success == true){ + this.dept = res.data.data.data + this.dept_last_page = res.data.data.last_page + for (let i=2;i<=this.dept_last_page;i++){ + HTTP.get(`departments?page=${i}`).then((res)=>{ + if (res.data.success == true){ + this.dept = this.dept.concat(res.data.data.data) + }}) + } + + }}) + }, + getJabatan(){ + this.jabatan = this.temp_jabatan + }, + getAtasan(){ + 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 + + }, + getData(){ + // di sini ambil data user dari backend + HTTP.get(`employees/${this.id}?include=user`).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.id = this.$route.params.id + this.getData() + + } + +} +</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/KaryawanEdit.vue b/src/views/KaryawanEdit.vue index 66e7a60c5b99c86c916d30b99decbf13135ae792..8ef90dba8f2929f33d5a5b7f297843cb3d89b7ad 100644 --- a/src/views/KaryawanEdit.vue +++ b/src/views/KaryawanEdit.vue @@ -184,17 +184,24 @@ export default { tanggal: "02-10-2020", }, ], + dept_last_page : 1, } }, 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 - }}) - }, + HTTP.get("departments?page=1").then((res)=>{ + if (res.data.success == true){ + this.dept = res.data.data.data + this.dept_last_page = res.data.data.last_page + for (let i=2;i<=this.dept_last_page;i++){ + HTTP.get(`departments?page=${i}`).then((res)=>{ + if (res.data.success == true){ + this.dept = this.dept.concat(res.data.data.data) + }}) + } + + }}) + }, getJabatan(){ this.jabatan = this.temp_jabatan }, @@ -271,7 +278,7 @@ export default { }, getData(){ // di sini ambil data user dari backend - HTTP.get(`employees/${this.id}`).then((res)=>{ + HTTP.get(`employees/${this.id}?include=user`).then((res)=>{ if (res.data.success){ this.employee = res.data.data console.log(res.data.data) diff --git a/src/views/KpiCreate.vue b/src/views/KpiCreate.vue index 5f251192772e5190a9ecf8b04ee95a75453a97bd..18201ffd5dce7418234dcab9e38617bbc250fc64 100644 --- a/src/views/KpiCreate.vue +++ b/src/views/KpiCreate.vue @@ -25,7 +25,7 @@ </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> + <textarea v-model="description" id="deskripsiInput" class="form-control" required></textarea> </div> <div class="mb-3 me-3"> <label for="typeInput" class="text-blue"> Periode </label> @@ -68,11 +68,11 @@ </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> + <input v-model="target" type="number" min="0" 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> + <input v-model="weight" type="number" min="0" 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" > diff --git a/src/views/KpiDetail.vue b/src/views/KpiDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..c57967ed67a4859874762fdb4022dac97eccc792 --- /dev/null +++ b/src/views/KpiDetail.vue @@ -0,0 +1,228 @@ +<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"> + <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> + <router-link :to="{ path: '/kpi' }" style="text-decoration: none"> + <p class="text-blue ms-1">kembali</p> + </router-link> + </div> + </div> + <div class="row p-3"> + <div class="col"> + <h1 class="text-blue">{{this.kpi.name}}</h1> + </div> + <hr class="my-2 " style="border: 1px solid #6992b4"> + </div> + <form 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> + <textarea v-model="kpi.description" id="deskripsiInput" class="form-control" readonly></textarea> + </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="kpi.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> + <button @click="responses " class="btn float-end">Responses</button> + </div> + </div> + </form> + <div class="row p-3"> + <h1 class="text-blue">Buat Response</h1> + <hr class="my-2"> + <form @submit="insert" autocomplete="off"> + <div class="mb-3 me-3"> + <label class="text-blue"> Nilai Aktual </label> + <input v-model="response.actual" type="number" min="0" class="form-control" required> + </div> + <div class="mb-3 me-3"> + <label class="text-blue"> Deskripsi </label> + <textarea v-model="response.description" class="form-control" required></textarea> + </div> + <div class="mb-3 me-3"> + <label class="text-blue"> Bukti </label> + <input type="file" accept="image/*" class="form-control" @change="onUpload" required> + </div> + <button type="submit" class="btn p-1" >Simpan</button> + </form> + </div> + + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import { HTTP } from '../http-common' +import 'vue-select/dist/vue-select.css' + + +export default { + name: "KpiDetail", + components: { + Header, + Sidebar, + }, + data() { + return{ + kpi : {}, + kpi_id: "", + response : { + kpi_id : -1, + actual : 0, + description : "", + status : "pending", + date : "", + images:[] + }, + } + }, + methods: { + 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")) + }, + insert(e){ + e.preventDefault(); + + let formData = new FormData(); + formData.append("kpi_id", this.response.kpi_id); + formData.append("actual", this.response.actual); + formData.append("description", this.response.description); + formData.append("status", this.response.status); + formData.append("date", this.response.date); + formData.append("images[0]", this.response.images[0]); + HTTP.post("kpi-responses", formData).then(()=>{ + alert("Berhasil Membuat Response") + }).catch((e)=>{ + console.log(e.response.data) + alert("Gagal Membuat Response") + }) + }, + onUpload(e){ + const file = e.target.files[0] + this.response.images = [] + this.response.images.push(file) + // this.item.imageUrl = URL.createObjectURL(file) + }, + responses(){ + this.$router.push({ path: `/kpi/${this.kpi_id}/responses`}) + } + + + }, + mounted(){ + this.kpi_id = this.$route.params.id + this.response.kpi_id = this.kpi_id + let today = new Date; + this.response.date = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate() + this.getKpi() + } + +} +</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/KpiIndex.vue b/src/views/KpiIndex.vue index 2449e5b8a06e11180b3bfc1b7209d274ee6c608a..cfb15412989a64cb88be37dd2df4fcaa485a776e 100644 --- a/src/views/KpiIndex.vue +++ b/src/views/KpiIndex.vue @@ -10,18 +10,45 @@ 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> + @change="changeFilter"> + <option value="daily" selected>Harian</option> + <option value="weekly">Mingguan</option> + <option value="monthly">Bulanan</option> + <option value="custom">Custom</option> </select> </div> <template v-for="kpi in kpis" :key="kpi"> <KpiCard :kpi="kpi"></KpiCard> </template> + <div v-if="this.page_num>1" class="row p-3"> + <nav aria-label="Page navigation"> + <ul class="pagination justify-content-end"> + <li v-if="this.page > 1" class="page-item"> + <div class="page-link p-2" @click="prevPage()">Previous</div> + </li> + <li v-else class="page-item disabled"> + <div class="page-link p-2">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-2">{{ p }}</div> + </li> + <li v-else class="page-item"> + <div class="page-link p-2" @click="goPage(p)">{{ p }}</div> + </li> + </template> + <li v-if="this.page < this.page_num" class="page-item"> + <div class="page-link p-2" @click="nextPage()">Next</div> + </li> + <li v-else class="page-item disabled"> + <div class="page-link p-2">Next</div> + </li> + </ul> + </nav> + </div> </div> </div> + </div> </template> @@ -30,6 +57,7 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" import KpiCard from "../components/kpiCard" +import { HTTP } from '../http-common' export default { @@ -52,19 +80,52 @@ export default { deadline: null, deadline_time: "23:00", }, - filter:"Harian", + filter:"daily", kpis: [], - + page : 1, + page_num : 0, + entry_per_page: 10, + pagination: [], } }, 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) + HTTP.get(`kpis/assigned?page=${this.page}&filter[period_type]=${this.filter}`).then((res)=>{ + this.kpis = 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(); + }) + }, + changeFilter(){ + this.page = 1 + this.getKpi() + }, + nextPage() { + if (this.page < this.page_num) { + this.page += 1; + this.getKpi() } + }, + prevPage() { + if (this.page > 1) { + this.page -= 1; + this.getKpi() + } + }, + goPage(i) { + this.page = i; + this.getKpi() + }, + 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); + } + } + }, }, mounted(){ this.getKpi() @@ -84,5 +145,9 @@ body { background-color: #fff; } +li { + padding: 0; +} + </style> \ No newline at end of file diff --git a/src/views/KpiManagerDetail.vue b/src/views/KpiManagerDetail.vue index 143ea79aa2522c4ae78c598556d62cea5efd956b..6c197b0d3c3609c93d949fddb0de3d49ca0be929 100644 --- a/src/views/KpiManagerDetail.vue +++ b/src/views/KpiManagerDetail.vue @@ -25,7 +25,7 @@ </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> + <textarea v-model="kpi.description" id="deskripsiInput" class="form-control" readonly></textarea> </div> <div class="mb-3 me-3"> <label for="typeInput" class="text-blue"> Periode </label> @@ -41,7 +41,7 @@ <option value="custom">Custom</option> </select> </div> - <div v-show="period_type == 'Custom'" class="mb-3 me-3"> + <div v-show="kpi.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> @@ -99,6 +99,7 @@ :filter="filter" :edit= false @delete-entry="detach" + @detail-entry="detail" ></Table> </div> @@ -153,7 +154,7 @@ export default { name : "Nama", data : "name", hidden : false, - clickable : false + clickable : true }, }, assignees: [], @@ -201,6 +202,10 @@ export default { assign(){ //for all selected_employee attach kpi //tambahin ke employees + if (this.selected_employee.length == 0){ + alert("Tidak ada karyawan yang dipilih") + return + } let emp_ids = {employee_ids: []} this.selected_employee.forEach((emp) => { this.assignees.push(emp) @@ -223,27 +228,30 @@ export default { }, getBawahan(){ //get bawahan ke backend - this.employees = this.temp_employees - }, - getAssignee(){ - this.assignees = this.temp_assignees - this.getBawahan() - //get assignee ke backend then getBawahan + // this.employees = this.temp_employees + HTTP.get("managed-employees").then((res)=>{ + this.employees = res.data.data + }) }, getKpi(){ //get kpi dengan id kpi_id - HTTP.get(`kpis/${this.kpi_id}`).then((res)=>{ + HTTP.get(`kpis/${this.kpi_id}?include=employees`).then((res)=>{ console.log(res.data.data) this.kpi = res.data.data this.kpi.deadline_time = this.kpi.deadline_time.replace(".", ":") + this.assignees = this.kpi.employees }).catch(()=> alert("Error loading page")) + }, + detail(id){ + let emp = this.employees.find(e => e.id == id) + this.$router.push({ name: "ManagerKaryawanDetail", params: { id: emp.slug }}) } }, mounted(){ this.kpi_id = this.$route.params.id this.getKpi() - this.getAssignee() + this.getBawahan() } } diff --git a/src/views/ManagerKaryawanDetail.vue b/src/views/ManagerKaryawanDetail.vue index 5061b6ecd198b363ebcd83386ada989167ccb890..5cdda1cd98bfb91abe2d8074bbaa011f0ab0243f 100644 --- a/src/views/ManagerKaryawanDetail.vue +++ b/src/views/ManagerKaryawanDetail.vue @@ -14,7 +14,7 @@ </div> <div class="row p-2"> <div class="col"> - <h1 class="text-blue">NAMA</h1> + <h1 class="text-blue">{{emp.name}}</h1> </div> </div> <form @submit="report" autocomplete="off" id="formReport"> @@ -50,9 +50,8 @@ </div> </div> <div class="row p-2"> - <!-- Ganti endpoint --> <Table2 - :endpoint="'kpis'" + :endpoint="endpoint" :columns="columns" :filter="filter" :filter_column="'period_type'" @@ -71,7 +70,7 @@ import Table2 from "../components/table-2"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; -// import { HTTP } from "../http-common"; +import { HTTP } from "../http-common"; export default { @@ -117,20 +116,32 @@ export default { data: "custom"}, ], id: -1, + slug: "", emp: { name : "Nama Karyawan" }, + endpoint:"", + startReport : "", + endReport:"", } }, - mounted(){ - this.id = this.$route.params.id + created(){ + this.slug = this.$route.params.id + this.getEmp() }, methods: { openResponses(idKpi){ - this.$router.push({ name: "ResponseIndex", params: { kpiId: idKpi, empId:this.id}}); + this.$router.push({ name: "ResponseIndex", params: { kpiId: idKpi, empId:this.slug}}); }, report(){ //download report + }, + getEmp(){ + HTTP.get(`employees/${this.slug}`).then((res)=>{ + this.emp = res.data.data + this.endpoint = `kpis/assigned/${this.emp.id}` + console.log(this.emp) + }).catch(()=> {alert("Error loading page")}) } } diff --git a/src/views/ManagerKaryawanIndex.vue b/src/views/ManagerKaryawanIndex.vue index fcc8ee07cb70bb56c8782d6dfd29e2fdc7b18bda..2902c65b3478b0b15cbe073f65611f84e05f6357 100644 --- a/src/views/ManagerKaryawanIndex.vue +++ b/src/views/ManagerKaryawanIndex.vue @@ -10,15 +10,14 @@ </div> </div> <div class="row p-2"> - <Table2 - :endpoint="'kpis'" + <Table + :data="bawahan" :columns="columns" :filter="filter" - :filter_column="'period_type'" :edit = "false" :delete = "false" @detail-entry="detailKaryawan" - ></Table2> + ></Table> </div> </div> </div> @@ -27,10 +26,11 @@ <script> -import Table2 from "../components/table-2"; +// import Table2 from "../components/table-2"; +import Table from "../components/table"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; -// import { HTTP } from "../http-common"; +import { HTTP } from "../http-common"; export default { @@ -38,15 +38,16 @@ export default { components: { Header, Sidebar, - Table2 + Table }, data(){ return{ filter: "", + bawahan: [], columns: { Id: { name : "Id", - data : "id", + data : "slug", hidden : true, clickable : false }, @@ -56,9 +57,9 @@ export default { hidden : false, clickable : true }, - Periode: { - name : "Periode", - data : "period_type", + Posisi: { + name : "Posisi", + data : "position", hidden : false, clickable : false }, @@ -68,9 +69,18 @@ export default { methods: { detailKaryawan(id){ //routing page detail - console.log(id) + this.$router.push({ name: "ManagerKaryawanDetail", params: { id: id }}); + }, + getBawahan(){ + HTTP.get("managed-employees").then((res)=>{ + console.log(res.data.data) + this.bawahan =res.data.data + }).catch(()=>alert("Error loading page")) } - } + }, + mounted(){ + this.getBawahan() + }, } </script> diff --git a/src/views/ResponseDetail.vue b/src/views/ResponseDetail.vue index 588a496d535bdf3850519b5e2d1f40d18395d734..8787d51a2dc4c94ccd3cbebb66b5b979bc158b21 100644 --- a/src/views/ResponseDetail.vue +++ b/src/views/ResponseDetail.vue @@ -13,7 +13,17 @@ </router-link> </div> <div class="row p-2"> - <h1 class="text-blue">{{kpi.name}}</h1> + <div class="col"> + <h1 class="text-blue">{{this.emp.name}}</h1> + </div> + </div> + <hr class="m-2"> + <div class="row px-2"> + <div class="col"> + <h3 class="text-blue">{{this.kpi.name}}</h3> + </div> + </div> + <div class="row p-2"> <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> @@ -28,16 +38,21 @@ </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> + <textarea v-model="response.description" type="text" id="NameInput" class="form-control" readonly> </textarea> </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 class="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Bukti: </label> + <a class="mx-2" :href="response.images[0].image">link</a> + </div> + <div v-if="response.status == 'pending'" class="col"> + <!-- <div class="col"> --> + <button @click="reject" class="btn-reject float-end mx-2">Reject</button> + <button @click="adjust" class="btn-blue float-end mx-2">Adjust</button> + <button @click="Reject" class="btn-green float-end mx-2">Approve</button> </div> </div> <hr class="m-2"> @@ -46,7 +61,7 @@ <h1 class="text-blue">Komentar</h1> </div> <div class="col"> - <button class="btn-blue float-end">Buat Komentar</button> + <button class="btn-blue float-end" data-bs-toggle="modal" data-bs-target="#commentModal" id="commentButton">Buat Komentar</button> </div> </div> <template v-for="c in comments" :key="c"> @@ -55,6 +70,43 @@ </div> </div> </div> + <!-- Adjust Modal --> + <button hidden="hidden" type="button" class="btn" data-bs-toggle="modal" data-bs-target="#adjustModal" id="modalButton"> + Launch modal + </button> + <div class="modal fade" id="adjustModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered mx-auto"> + <div class="modal-content"> + <div class="modal-body"> + <div class="mb-3 me-3 p-2"> + <label class="text-blue"> Nilai Aktual </label> + <input v-model="response.actual" type="number" min="0" class="form-control" required> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn-red m-2" data-bs-dismiss="modal">Tutup</button> + <button type="submit" class="btn m-2" data-bs-dismiss="modal" @click="saveAdj">Simpan</button> + </div> + </div> + </div> + </div> + <!-- Comment Modal --> + <div class="modal fade" id="commentModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered mx-auto"> + <div class="modal-content"> + <div class="modal-body"> + <div class="mb-3 me-3 p-2"> + <label class="text-blue"> Komentar </label> + <textarea v-model="newComment" type="number" min="0" class="form-control" required></textarea> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn-red m-2" data-bs-dismiss="modal">Tutup</button> + <button type="submit" class="btn m-2" data-bs-dismiss="modal" @click="postComment">Simpan</button> + </div> + </div> + </div> + </div> </template> <script> @@ -62,14 +114,15 @@ import Header from "../components/header" import Sidebar from "../components/sidebar" import CommentCard from "../components/commentCard" - +import { HTTP } from "../http-common"; export default { name: "ResponseDetail", components: { Header, Sidebar, - CommentCard + CommentCard, + // VueFinalModal }, data(){ return{ @@ -85,25 +138,85 @@ export default { date: "10-23-2021", actual: 10, description: "Deskripsi", - status: "Pending" + status: "Pending", + images: [{image: ""}] }, kpi:{ name: "Nama KPI", target: 20 - } - + }, + emp: {}, + responseAdj:{}, + newComment:"" } }, created(){ this.empId = this.$route.params.empId this.kpiId = this.$route.params.kpiId this.responseId = this.$route.params.responseId + this.getEmpId() + this.getResponse() this.getComments() }, methods: { getComments(){ - this.comments.push(this.temp_comment) - this.comments.push(this.temp_comment) + // this.comments.push(this.temp_comment) + // this.comments.push(this.temp_comment) + HTTP.get(`kpi-responses/${this.responseId}/comments`).then((res)=>{ + this.comments = res.data.data.data + }) + }, + getEmpId(){ + HTTP.get(`employees/${this.empId}`).then((res)=>{ + this.emp = res.data.data + this.getKpiName() + }) + }, + getKpiName(){ + HTTP.get(`kpis/${this.kpiId}`).then(res=>{ + this.kpi = res.data.data + }) + }, + getResponse(){ + HTTP.get(`kpi-responses/${this.responseId}`).then((res)=>{ + this.response = res.data.data + }).catch(()=>{alert("Error loading page")}) + }, + changeStatus(id, status){ + let response = { + "kpi_id": this.response.kpi_id, + "actual" : this.responseId.actual, + "description" : this.responseId.description, + "status" : status, + "date" : this.responseId.date + } + HTTP.put(`kpi-responses/${id}`, response).then(()=>{ + this.refresh = !this.refresh + }).catch(()=> {alert("Gagal Mengubah Status")}) + }, + approve(){ + this.changeStatus(this.responseId, "approved") + }, + reject(){ + this.changeStatus(this.responseId, "rejected") + }, + adjust(){ + document.getElementById("modalButton").click() + }, + saveAdj(){ + HTTP.put(`kpi-responses/${this.responseId}`, this.responseAdj).then(()=>{ + this.refresh = !this.refresh + }).catch(()=> {alert("Gagal Mengubah Nilai")}) + }, + postComment(){ + let comment = { + kpi_response_id : this.responseId, + comment : this.newComment + } + HTTP.post(`kpi-comments`, comment).then(()=>{ + this.getComments() + this.newComment = "" + }).catch(()=> {alert("Gagal membuat komentar")}) } }, } @@ -141,4 +254,18 @@ body { border-radius: 15px; padding: 7px; } + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + +.btn-red { + background-color: #f4476b; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} </style> \ No newline at end of file diff --git a/src/views/ResponseEmployee.vue b/src/views/ResponseEmployee.vue new file mode 100644 index 0000000000000000000000000000000000000000..d2e52d7b672c625457d093949634f0250c666430 --- /dev/null +++ b/src/views/ResponseEmployee.vue @@ -0,0 +1,218 @@ +<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: `/kpi/${this.kpiId}/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"> + <div class="col"> + <h1 class="text-blue">{{this.emp.name}}</h1> + </div> + </div> + <hr class="m-2"> + <div class="row px-2"> + <div class="col"> + <h3 class="text-blue">{{this.kpi.name}}</h3> + </div> + </div> + <div class="row p-2"> + <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> + <textarea v-model="response.description" type="text" id="NameInput" class="form-control" readonly> </textarea> + </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="mb-3 me-3"> + <label for="NameInput" class="text-blue"> Bukti: </label> + <a class="mx-2" :href="response.images[0].image">link</a> + </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" data-bs-toggle="modal" data-bs-target="#commentModal" id="commentButton">Buat Komentar</button> + </div> + </div> + <template v-for="c in comments" :key="c"> + <CommentCard :comment="c"></CommentCard> + </template> + </div> + </div> + </div> + <!-- Comment Modal --> + <div class="modal fade" id="commentModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered mx-auto"> + <div class="modal-content"> + <div class="modal-body"> + <div class="mb-3 me-3 p-2"> + <label class="text-blue"> Komentar </label> + <textarea v-model="newComment" type="number" min="0" class="form-control" required></textarea> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn-red m-2" data-bs-dismiss="modal">Tutup</button> + <button type="submit" class="btn m-2" data-bs-dismiss="modal" @click="postComment">Simpan</button> + </div> + </div> + </div> + </div> +</template> + +<script> + +import Header from "../components/header" +import Sidebar from "../components/sidebar" +import CommentCard from "../components/commentCard" +import { HTTP } from "../http-common"; + +export default { + name: "ResponseEmployee", + components: { + Header, + Sidebar, + CommentCard, + // VueFinalModal + }, + 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", + images: [{image: ""}] + }, + kpi:{ + name: "Nama KPI", + target: 20 + }, + emp: {}, + responseAdj:{}, + newComment:"" + } + }, + created(){ + this.kpiId = this.$route.params.kpiId + this.responseId = this.$route.params.responseId + this.getEmpId() + this.getResponse() + this.getComments() + }, + methods: { + getComments(){ + // this.comments.push(this.temp_comment) + // this.comments.push(this.temp_comment) + HTTP.get(`kpi-responses/${this.responseId}/comments`).then((res)=>{ + this.comments = res.data.data.data + }) + }, + getEmpId(){ + HTTP.get('users/profile').then((res)=>{ + this.emp = res.data.data.profile + this.getKpiName() + }) + }, + getKpiName(){ + HTTP.get(`kpis/${this.kpiId}`).then(res=>{ + this.kpi = res.data.data + }) + }, + getResponse(){ + HTTP.get(`kpi-responses/${this.responseId}`).then((res)=>{ + this.response = res.data.data + }).catch(()=>{alert("Error loading page")}) + }, + postComment(){ + let comment = { + kpi_response_id : this.responseId, + comment : this.newComment + } + HTTP.post(`kpi-comments`, comment).then(()=>{ + this.getComments() + this.newComment = "" + }).catch(()=> {alert("Gagal membuat komentar")}) + } + }, +} +</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; +} + +.btn { + background-color: #6992b4; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} + +.btn-red { + background-color: #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 index fddb71adfb4bf21094b4e462c987a5de51d3e040..2b53667cc074f0c4eb62f5076862887b78e86727 100644 --- a/src/views/ResponseIndex.vue +++ b/src/views/ResponseIndex.vue @@ -4,8 +4,17 @@ <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 v-if="isManager" class="row p-3"> + <router-link :to="{ path: `/manager/employees/${this.slug}` }" 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 v-else class="row p-3"> + <!-- Ganti Path --> + <router-link :to="{ path: `/kpi/${this.kpiId}` }" 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> @@ -14,20 +23,114 @@ </div> <div class="row p-2"> <div class="col"> - <h1 class="text-blue">Nama KPI</h1> + <h1 class="text-blue">{{this.emp.name}}</h1> + </div> + </div> + <hr class="m-2"> + <div class="row px-2"> + <div class="col"> + <h3 class="text-blue">{{this.kpi.name}}</h3> </div> </div> + <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> + <textarea v-model="kpi.description" id="deskripsiInput" class="form-control" readonly></textarea> + </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="kpi.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> + <!-- Button trigger modal --> + <button v-if="isManager" hidden="hidden" type="button" class="btn" data-bs-toggle="modal" data-bs-target="#adjustModal" id="modalButton"> + Launch modal + </button> + <div v-if="isManager" class="modal fade" id="adjustModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog modal-dialog-centered mx-auto"> + <div class="modal-content"> + <div class="modal-body"> + <div class="mb-3 me-3 p-2"> + <label class="text-blue"> Nilai Aktual </label> + <input v-model="responseAdj.actual" type="number" min="0" class="form-control" required> + </div> + </div> + <div class="modal-footer"> + <button type="button" class="btn-red m-2" data-bs-dismiss="modal">Tutup</button> + <button type="submit" class="btn m-2" data-bs-dismiss="modal" @click="saveAdj">Simpan</button> + </div> + </div> + </div> + </div> <div class="row p-2"> - <Table2 - :endpoint="`kpis/${this.kpiId}/employees/${this.empId}/kpi-responses`" + <div class="col"> + <h3 class="text-blue">Responses</h3> + </div> + </div> + <div class="row p-2"> + <TableR + :endpoint="endpoint" :columns="columns" :filter="filter" :filter_column="'status'" - :edit = "false" - :delete = "false" :default_sort = "'status'" + :refresh = "refresh" + :isManager="isManager" @detail-entry="detailResponse" - ></Table2> + @approve="approve" + @reject="reject" + @adjust="adjust" + ></TableR> </div> </div> </div> @@ -36,10 +139,11 @@ <script> -import Table2 from "../components/table-2"; +import TableR from "../components/table-response"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; -// import { HTTP } from "../http-common"; +import { HTTP } from "../http-common"; +// import {VueFinalModal} from 'vue-final-modal'; export default { @@ -47,7 +151,11 @@ export default { components: { Header, Sidebar, - Table2 + TableR, + // VueFinalModal + }, + props:{ + isManager : Boolean }, data(){ return{ @@ -65,7 +173,7 @@ export default { hidden : false, clickable : true }, - Nila: { + Nilai: { name : "Nilai", data : "actual", hidden : false, @@ -77,19 +185,101 @@ export default { hidden : false, clickable : false }, + Bukti: { + name : "Bukti", + data : "images", + hidden : false, + clickable : false + }, }, - empId:-1, + emp:{}, kpiId:-1, + slug: "", + endpoint:"", + kpi:{}, + refresh:true, + showModal : false, + responseAdj : {actual : 0}, + responseId : -1, } }, created(){ - this.empId = this.$route.params.empId + if(this.isManager){ + this.slug = this.$route.params.empId + } this.kpiId = this.$route.params.kpiId + this.getEmpId() }, + methods: { detailResponse(id){ //routing page detail - this.$router.push({ name: "ResponseDetail", params: { kpiId: this.kpiId, empId:this.empId, responseId:id }}); + if (this.isManager){ + this.$router.push({ name: "ResponseDetail", params: { kpiId: this.kpiId, empId:this.slug, responseId:id }}); + }else{ + this.$router.push({ name: "ResponseEmployee", params: { kpiId: this.kpiId, responseId:id }}); + } + }, + getEmpId(){ + if (this.isManager){ + HTTP.get(`employees/${this.slug}`).then((res)=>{ + this.emp = res.data.data + this.getKpiName() + }) + }else{ + HTTP.get('users/profile').then((res)=>{ + this.emp = res.data.data.profile + this.getKpiName() + }) + } + }, + getKpiName(){ + HTTP.get(`kpis/${this.kpiId}`).then(res=>{ + this.kpi = res.data.data + this.endpoint = `kpis/${this.kpi.id}/employees/${this.emp.id}/kpi-responses` + }) + }, + changeStatus(id, status){ + HTTP.get(`kpi-responses/${id}`).then((res)=>{ + let kpiRes = res.data.data + kpiRes.status = status + let response = { + "kpi_id": kpiRes.kpi_id, + "actual" : kpiRes.actual, + "description" : kpiRes.description, + "status" : status, + "date" : kpiRes.date + } + HTTP.put(`kpi-responses/${id}`, response).then(()=>{ + this.refresh = !this.refresh + }).catch(()=> {alert("Gagal Mengubah Status")}) + }) + }, + approve(id){ + this.changeStatus(id, "approved") + }, + reject(id){ + this.changeStatus(id, "rejected") + }, + adjust(id){ + HTTP.get(`kpi-responses/${id}`).then((res)=>{ + let kpiRes = res.data.data + kpiRes.status = "approved" + this.responseAdj = { + "kpi_id": kpiRes.kpi_id, + "actual" : kpiRes.actual, + "description" : kpiRes.description, + "status" : kpiRes.status, + "date" : kpiRes.date + } + this.responseId = id + document.getElementById("modalButton").click() + }) + }, + saveAdj(){ + HTTP.put(`kpi-responses/${this.responseId}`, this.responseAdj).then(()=>{ + this.refresh = !this.refresh + }).catch(()=> {alert("Gagal Mengubah Nilai")}) } } @@ -115,5 +305,11 @@ body { padding: 7px; } +.btn-red { + background-color: #f4476b; + color: #f1f7fc; + border-radius: 15px; + padding: 7px; +} </style> \ No newline at end of file diff --git a/src/views/UserIndex.vue b/src/views/UserIndex.vue index 12494f3ac1b6602501d362623a26d5eeafd71bbb..20ab5831ae188a27a5e1355e691d99105998f388 100644 --- a/src/views/UserIndex.vue +++ b/src/views/UserIndex.vue @@ -18,14 +18,14 @@ <div class="row p-2"> <div class="col p-2"> <select - v-if="filter_type == 'Departemen'" + v-if="filter_type == 'department_id'" @change="getUsers" v-model="selected_filter" class="form-select" aria-label="Default select example" > - <option selected>Semua</option> - <option v-for="d in depts" :key="d.id" :value="d.name">{{d.name}}</option> + <option value="" selected>Semua</option> + <option v-for="d in depts" :key="d.id" :value="d.id">{{d.name}}</option> </select> <select v-else @@ -34,9 +34,8 @@ class="form-select" aria-label="Default select example" > - <option selected>Semua</option> - <option value="Manager">Manager</option> - <option value="Staf">Staf</option> + <option value="" selected>Semua</option> + <option v-for="j in jabatan" :key="j" :value="j">{{j}}</option> </select> </div> <div class="col p-2"> @@ -45,12 +44,12 @@ class="form-select" aria-label="Default select example" > - <option selected>Departemen</option> - <option value="Jabatan">Jabatan</option> + <option value="department_id" selected>Departemen</option> + <option value="position">Jabatan</option> </select> </div> <div class="col p-2"> - <div class="search float-end"> + <!-- <div class="search float-end"> <div class="row"> <div class="col-10"> <input @@ -67,11 +66,22 @@ ></i> </div> </div> - </div> + </div> --> </div> </div> <div class="row p-2"> - <Table :data="users" :columns="columns" :filter="filter" @delete-entry="deleteUser" @edit-entry="editUser" @detail-entry="detailUser"></Table> + <Table2 + :endpoint="'employees'" + :columns="columns" + :filter="selected_filter" + :filter_column="filter_type" + :key = "componentKey" + :default_sort="'name'" + :include="'user'" + @edit-entry="editUser" + @delete-entry="deleteUser" + @detail-entry="detailUser" + ></Table2> </div> </div> </div> @@ -80,7 +90,7 @@ </template> <script> -import Table from "../components/table"; +import Table2 from "../components/table-2"; import Header from "../components/header"; import Sidebar from "../components/sidebar"; import { HTTP } from "../http-common"; @@ -88,7 +98,7 @@ import { HTTP } from "../http-common"; export default { name: "UserIndex", components: { - Table, + Table2, Header, Sidebar, }, @@ -109,12 +119,6 @@ export default { hidden : false, clickable : true }, - Email: { - name : "Email", - data : "email", - hidden : false, - clickable : false - }, Jabatan: { name : "Jabatan", data : "position", @@ -127,40 +131,55 @@ export default { hidden : false, clickable : false }, - Tanggal: { - name : "Tanggal", - data : "tanggal", - hidden : false, - clickable : false - }, + User: { + name : "User", + data : 'user', + hidden : true, + clickable : false, + child : { + Email: { + name : "Email", + data : "email", + hidden : false, + clickable : false + }, + Tanggal: { + name : "Tanggal", + data : "created_at", + hidden : false, + clickable : false + }, + } + } }, filter: "", - filter_type: "Departemen", - selected_filter: "Semua", + filter_type: "department_id", + selected_filter: "", + dept_last_page: 1, + jabatan: [] }; }, methods: { - getUsers() { - - // di sini ambil data user dari database - 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)=>{ + HTTP.get("departments?page=1").then((res)=>{ if (res.data.success == true){ - this.depts = res.data.data + this.depts = res.data.data.data + this.dept_last_page = res.data.data.last_page + for (let i=2;i<=this.dept_last_page;i++){ + HTTP.get(`departments?page=${i}`).then((res)=>{ + if (res.data.success == true){ + this.depts = this.depts.concat(res.data.data.data) + }}) + } + }}) }, + getJabatan(){ + HTTP.get("employee-positions").then((res)=>{ + this.jabatan = res.data.data + }) + console.log(this.jabatan) + }, filter_user() { if (this.selected_filter != "Semua") { @@ -172,7 +191,7 @@ export default { } }, detailUser(id){ - console.log(id) + this.$router.push({ name: "KaryawanDetail", params: { id: id }}); }, deleteUser(id){ if (confirm("Yakin akan menghapus karyawan?")){ @@ -193,8 +212,8 @@ export default { } }, mounted() { - this.getUsers() this.getDept() + this.getJabatan() }, }; </script>