diff --git a/package-lock.json b/package-lock.json index 5b210b03d8b571be4a6fb1439e459faa6da85468..67f7720d8f38f07390dc4520e8c0863b2ca5b714 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "bootstrap": "^5.1.3", "core-js": "^3.6.5", "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", @@ -14013,6 +14014,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", @@ -26596,6 +26605,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 19533c04b4d95b9df0dc545b57ba20e7b1b97701..cda5f0833934a0a709220525a6d1b9386e641527 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "bootstrap": "^5.1.3", "core-js": "^3.6.5", "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/components/table.vue b/src/components/table.vue index 1a07429ef84d5bfc02507cd815a7973900dd2a56..bd7c946e3f70d5a06fd3d2e7f7f0b40b248f8e28 100644 --- a/src/components/table.vue +++ b/src/components/table.vue @@ -12,7 +12,7 @@ {{ c.name}} </th> </template> - <th class="text-blue">Aksi</th> + <th v-if="this.edit || this.delete" class="text-blue">Aksi</th> </tr> </thead> <tbody> @@ -36,8 +36,8 @@ </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> + <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> @@ -77,6 +77,14 @@ export default { //map nama kolom ke data columns: Object, filter: String, + edit: { + type: Boolean, + default: true + }, + delete: { + type: Boolean, + default: true + }, }, data() { return { diff --git a/src/router/index.js b/src/router/index.js index aac1cad46e11100149759301af998c0a31c719d9..73232584641d938abe454eb53b241191cfde718d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,5 +1,5 @@ import { createRouter, createWebHistory } from "vue-router"; -import Home from "../views/Home.vue"; +// import Home from "../views/Home.vue"; import uProfil from "../views/uProfil.vue"; import aProfil from "../views/aProfil.vue"; import Login from "../views/Login.vue"; @@ -11,6 +11,8 @@ 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"; @@ -20,7 +22,7 @@ const routes = [ { path: "/", name: "Home", - component: Home, + component: Login, }, { path: "/login", @@ -77,6 +79,16 @@ const routes = [ 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", diff --git a/src/views/KpiCreate.vue b/src/views/KpiCreate.vue new file mode 100644 index 0000000000000000000000000000000000000000..13c5f9026409a97cf341a3184287ea3a515c0704 --- /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: '/admin' }" 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="Persentase">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/KpiManagerDetail.vue b/src/views/KpiManagerDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..858cfca3068cc4ef9040927ae814181d6eb81a15 --- /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: '/admin' }" 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.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.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="precentage">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.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 : { + emp_id: 1, + kpi_name: "Kebersihan", + description: "Lingkungan Salman Bersih", + target: 100, + inverted: false, + target_type: "Persentase", + weight: 20, + period_type: "Harian", + deadline: "", + deadline_time: "23:00", + }, + 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(){ + this.kpi = this.temp_kpi + //get kpi dengan id kpi_id + // HTTP.get(`kpis/${this.kpi_id}`).then((res)=>{ + // console.log(res.data) + // this.kpi = res.data + // }).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/Login.vue b/src/views/Login.vue index bb757d84bf6825fb606c283e205ece7267781696..049e6735a56c711eb85cebb2fc2ce2f0bc44a0e2 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -109,8 +109,7 @@ export default { }) .then((response) => { if (response.data.success) { - localStorage.setItem("token", response.data.token) - this.$router.push({ name: "Home" }); + this.$router.push({ name: "User" }); } }) .catch(() => alert("username atau password salah"));