diff --git a/src/components/kpiCard.vue b/src/components/kpiCard.vue index e63b0f6bd4e0df534b8787f706b1d02d38997f1b..6a036c3f38363224961959407082bb5586d4c3ca 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: "KpiManagerDetail", params: { id: this.kpi.id }}); + } }, }; </script> diff --git a/src/components/table-2.vue b/src/components/table-2.vue index 10cc597e831863a53d8c3c945670b1fd38223c16..9bd4807087858d092e1ecb30ad8412f54fb2b3ae 100644 --- a/src/components/table-2.vue +++ b/src/components/table-2.vue @@ -43,7 +43,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> @@ -115,12 +115,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: { @@ -131,7 +137,7 @@ export default { } 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) + // 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 diff --git a/src/components/table-response.vue b/src/components/table-response.vue new file mode 100644 index 0000000000000000000000000000000000000000..e0431ff6f8c6fa53dacbffd7a419f41a8f45813c --- /dev/null +++ b/src/components/table-response.vue @@ -0,0 +1,220 @@ +<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 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 style="text-align: center"> + <button @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 @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 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 { + 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, + }, + 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() + } + }, + 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 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 95314beaecbf50a05d9b1960038a58cbb85c5051..c6918968b32be11e48b2566825e5311639c638f5 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -81,6 +81,11 @@ const routes = [ name: "kpiIndex", component: KpiIndex, }, + { + path: "/kpi/:id", + name: "KpiDetail", + component: KpiIndex, + }, { path: "/manager/kpi/create", name: "KpiCreate", 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..06d1e2b1bbdbca291fd6d1c2db79930558fe2974 100644 --- a/src/views/KpiManagerDetail.vue +++ b/src/views/KpiManagerDetail.vue @@ -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/Login.vue b/src/views/Login.vue index 049e6735a56c711eb85cebb2fc2ce2f0bc44a0e2..69d6570d7c643a4d1276f5bab1bcf5daca187a0d 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -108,6 +108,7 @@ export default { password: this.password, }) .then((response) => { + localStorage.setItem("token", response.data.token) if (response.data.success) { this.$router.push({ name: "User" }); } 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/ResponseIndex.vue b/src/views/ResponseIndex.vue index fddb71adfb4bf21094b4e462c987a5de51d3e040..83a19542bb76cb1e5a1a42badeeffbed19773f8c 100644 --- a/src/views/ResponseIndex.vue +++ b/src/views/ResponseIndex.vue @@ -5,7 +5,7 @@ <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"> + <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> @@ -14,12 +14,84 @@ </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> + <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="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> + <div class="row p-2"> + <div class="col"> + <h3 class="text-blue">Responses</h3> </div> </div> <div class="row p-2"> - <Table2 - :endpoint="`kpis/${this.kpiId}/employees/${this.empId}/kpi-responses`" + <TableR + :endpoint="endpoint" :columns="columns" :filter="filter" :filter_column="'status'" @@ -27,7 +99,7 @@ :delete = "false" :default_sort = "'status'" @detail-entry="detailResponse" - ></Table2> + ></TableR> </div> </div> </div> @@ -36,10 +108,10 @@ <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"; export default { @@ -47,7 +119,7 @@ export default { components: { Header, Sidebar, - Table2 + TableR }, data(){ return{ @@ -65,7 +137,7 @@ export default { hidden : false, clickable : true }, - Nila: { + Nilai: { name : "Nilai", data : "actual", hidden : false, @@ -78,18 +150,34 @@ export default { clickable : false }, }, - empId:-1, + emp:{}, kpiId:-1, + slug: "", + endpoint:"", + kpi:{}, } }, created(){ - this.empId = this.$route.params.empId + 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 }}); + }, + getEmpId(){ + HTTP.get(`employees/${this.slug}`).then((res)=>{ + this.emp = res.data.data + 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` + }) } }