301 lines
10 KiB
Vue
301 lines
10 KiB
Vue
<template>
|
|
<v-layout class="mb-2" column>
|
|
<v-dialog v-model="dialogsuccess" persistent max-width="320">
|
|
<v-card>
|
|
<v-card-title class="headline success white--text">Berhasil</v-card-title>
|
|
<v-card-text>{{ msgsuccess }}</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn color="primary" flat @click="closeDialogSuccess">OK</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
|
|
<v-card>
|
|
<v-subheader red--text text--lighten-1>
|
|
DASHBOARD USER MCU
|
|
<v-spacer></v-spacer>
|
|
<v-btn small color="warning" @click="newForm">Baru</v-btn>
|
|
<v-btn small color="primary" @click="saveUser">Simpan</v-btn>
|
|
<v-btn small color="info" @click="resetPassword">Reset Password</v-btn>
|
|
<v-btn small color="error" :disabled="!isEditMode" @click="removeUser">Hapus User</v-btn>
|
|
</v-subheader>
|
|
<v-divider></v-divider>
|
|
|
|
<v-layout row wrap class="pa-2">
|
|
<v-flex xs12 pa-1>
|
|
<v-text-field label="Username*" :readonly="isEditMode" v-model="username"></v-text-field>
|
|
</v-flex>
|
|
<v-flex xs12 pa-1>
|
|
<v-text-field label="Display Name" v-model="displayName"></v-text-field>
|
|
</v-flex>
|
|
<v-flex xs12 pa-1 v-if="!isEditMode">
|
|
<v-text-field
|
|
label="Password*"
|
|
type="password"
|
|
hint="Minimal 8 karakter, wajib huruf besar, huruf kecil, angka, dan simbol"
|
|
persistent-hint
|
|
:success="passwordReady"
|
|
:error="passwordTouched && !passwordReady"
|
|
:error-messages="passwordTouched && !passwordReady ? ['Password belum memenuhi aturan'] : []"
|
|
@blur="passwordTouched = true"
|
|
v-model="password"
|
|
></v-text-field>
|
|
<div class="caption mt-1">
|
|
<span :class="passwordChecks.length ? 'green--text' : 'red--text'">• Minimal 8 karakter</span><br>
|
|
<span :class="passwordChecks.upper ? 'green--text' : 'red--text'">• Ada huruf besar (A-Z)</span><br>
|
|
<span :class="passwordChecks.lower ? 'green--text' : 'red--text'">• Ada huruf kecil (a-z)</span><br>
|
|
<span :class="passwordChecks.number ? 'green--text' : 'red--text'">• Ada angka (0-9)</span><br>
|
|
<span :class="passwordChecks.symbol ? 'green--text' : 'red--text'">• Ada simbol</span>
|
|
</div>
|
|
</v-flex>
|
|
|
|
<v-flex xs12 pa-1>
|
|
<v-autocomplete
|
|
v-model="selectedProject"
|
|
:items="projectItems"
|
|
:search-input.sync="projectKeyword"
|
|
item-text="label"
|
|
return-object
|
|
label="Cari Project (Nomor / Nama)"
|
|
no-filter
|
|
></v-autocomplete>
|
|
</v-flex>
|
|
<v-flex xs12 pa-1>
|
|
<v-btn small color="primary" @click="assignProject">Assign Project</v-btn>
|
|
</v-flex>
|
|
|
|
<v-flex xs12 pa-1>
|
|
<div class="caption mb-1">Project Assigned</div>
|
|
<v-chip
|
|
v-for="(p, idx) in assignedProjects"
|
|
:key="idx"
|
|
class="mr-1 mb-1"
|
|
close
|
|
@input="removeProject(p)"
|
|
>
|
|
{{ p.project_number }} - {{ p.project_name }}
|
|
</v-chip>
|
|
<div v-if="assignedProjects.length == 0" class="grey--text caption">Belum ada project</div>
|
|
</v-flex>
|
|
</v-layout>
|
|
</v-card>
|
|
</v-layout>
|
|
</template>
|
|
|
|
<script>
|
|
module.exports = {
|
|
data() {
|
|
return {
|
|
username: '',
|
|
displayName: '',
|
|
password: '',
|
|
passwordTouched: false,
|
|
selectedProject: null,
|
|
projectKeyword: '',
|
|
pendingProjects: []
|
|
}
|
|
},
|
|
computed: {
|
|
selectedUser() { return this.$store.state.dashboard_user.selected_user || {} },
|
|
isEditMode() { return !!(this.selectedUser && this.selectedUser.User_ID) },
|
|
assignedProjects() { return this.isEditMode ? (this.selectedUser.projects || []) : this.pendingProjects },
|
|
dialogsuccess() { return this.$store.state.dashboard_user.dialog_success },
|
|
msgsuccess() { return this.$store.state.dashboard_user.msg_success },
|
|
passwordChecks() {
|
|
let pwd = this.password || ''
|
|
return {
|
|
length: pwd.length >= 8,
|
|
upper: /[A-Z]/.test(pwd),
|
|
lower: /[a-z]/.test(pwd),
|
|
number: /[0-9]/.test(pwd),
|
|
symbol: /[^A-Za-z0-9]/.test(pwd)
|
|
}
|
|
},
|
|
passwordReady() {
|
|
return this.passwordChecks.length
|
|
&& this.passwordChecks.upper
|
|
&& this.passwordChecks.lower
|
|
&& this.passwordChecks.number
|
|
&& this.passwordChecks.symbol
|
|
},
|
|
projectItems() {
|
|
return (this.$store.state.dashboard_user.project_options || []).map(p => {
|
|
return Object.assign({}, p, { label: p.project_number + ' - ' + p.project_name })
|
|
})
|
|
}
|
|
},
|
|
watch: {
|
|
selectedUser: {
|
|
deep: true,
|
|
immediate: true,
|
|
handler(val) {
|
|
this.username = val.User_Username || ''
|
|
this.displayName = val.User_DisplayName || ''
|
|
this.password = ''
|
|
this.passwordTouched = false
|
|
if (val && val.User_ID) this.pendingProjects = []
|
|
}
|
|
},
|
|
projectKeyword(val, old) {
|
|
let next = (val || '').trim()
|
|
let prev = (old || '').trim()
|
|
if (next === prev) return
|
|
this.thrSearchProject(next)
|
|
}
|
|
},
|
|
methods: {
|
|
thrSearchProject: _.debounce(function(keyword) {
|
|
this.$store.dispatch('dashboard_user/search_project', { search: keyword || '' })
|
|
}, 300),
|
|
closeDialogSuccess() {
|
|
this.$store.commit('dashboard_user/update_dialog_success', false)
|
|
this.refreshList()
|
|
},
|
|
refreshList() {
|
|
this.$store.dispatch('dashboard_user/search', {
|
|
username: this.$store.state.dashboard_user.search_username || '',
|
|
project: this.$store.state.dashboard_user.search_project && this.$store.state.dashboard_user.search_project.mcu_id ? this.$store.state.dashboard_user.search_project.mcu_id : 'all',
|
|
page: this.$store.state.dashboard_user.page || 1,
|
|
limit: this.$store.state.dashboard_user.limit || 20
|
|
})
|
|
},
|
|
validateBasic(requirePassword) {
|
|
if (!this.username) {
|
|
alert('Username wajib diisi')
|
|
return false
|
|
}
|
|
if (requirePassword && !this.password) {
|
|
alert('Password wajib diisi')
|
|
return false
|
|
}
|
|
if (requirePassword) {
|
|
this.passwordTouched = true
|
|
let pwd = this.password || ''
|
|
if (pwd.length < 8) {
|
|
alert('Password minimal 8 karakter')
|
|
return false
|
|
}
|
|
if (!(/[A-Z]/.test(pwd) && /[a-z]/.test(pwd) && /[0-9]/.test(pwd) && /[^A-Za-z0-9]/.test(pwd))) {
|
|
alert('Password harus mengandung huruf besar, huruf kecil, angka, dan simbol')
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
async saveUser() {
|
|
if (!this.validateBasic(!this.isEditMode)) return
|
|
let resp = await this.$store.dispatch('dashboard_user/save', {
|
|
username: this.username,
|
|
password: this.password,
|
|
display_name: this.displayName
|
|
})
|
|
if (resp.status == 'OK') {
|
|
if (this.pendingProjects.length > 0) {
|
|
for (let i = 0; i < this.pendingProjects.length; i++) {
|
|
let p = this.pendingProjects[i]
|
|
await this.$store.dispatch('dashboard_user/assign_project', {
|
|
username: this.username,
|
|
mcu_id: p.mcu_id
|
|
})
|
|
}
|
|
this.pendingProjects = []
|
|
}
|
|
this.passwordTouched = false
|
|
this.$store.commit('dashboard_user/update_selected_user', {})
|
|
this.refreshList()
|
|
} else {
|
|
alert(resp.message || 'Gagal simpan user')
|
|
}
|
|
},
|
|
async resetPassword() {
|
|
this.password = window.prompt('Masukkan password baru') || ''
|
|
if (!this.validateBasic(true)) return
|
|
let resp = await this.$store.dispatch('dashboard_user/reset_password', {
|
|
username: this.username,
|
|
password: this.password
|
|
})
|
|
if (resp.status != 'OK') {
|
|
alert(resp.message || 'Gagal reset password')
|
|
}
|
|
},
|
|
async removeUser() {
|
|
if (!this.isEditMode || !this.username) {
|
|
alert('Pilih user dulu')
|
|
return
|
|
}
|
|
if (!window.confirm('Hapus user ini?')) return
|
|
let resp = await this.$store.dispatch('dashboard_user/remove_user', {
|
|
username: this.username
|
|
})
|
|
if (resp.status == 'OK') {
|
|
this.newForm()
|
|
this.refreshList()
|
|
} else {
|
|
alert(resp.message || 'Gagal hapus user')
|
|
}
|
|
},
|
|
async assignProject() {
|
|
if (!this.username) {
|
|
alert('Pilih atau isi username dulu')
|
|
return
|
|
}
|
|
if (!this.selectedProject || !this.selectedProject.mcu_id) {
|
|
alert('Pilih project dulu')
|
|
return
|
|
}
|
|
if (!this.isEditMode) {
|
|
let exists = this.pendingProjects.find(p => p.mcu_id == this.selectedProject.mcu_id)
|
|
if (exists) {
|
|
this.selectedProject = null
|
|
this.projectKeyword = ''
|
|
return
|
|
}
|
|
this.pendingProjects.push(this.selectedProject)
|
|
this.selectedProject = null
|
|
this.projectKeyword = ''
|
|
return
|
|
}
|
|
let resp = await this.$store.dispatch('dashboard_user/assign_project', {
|
|
username: this.username,
|
|
mcu_id: this.selectedProject.mcu_id
|
|
})
|
|
if (resp.status == 'OK') {
|
|
this.selectedProject = null
|
|
this.projectKeyword = ''
|
|
this.refreshList()
|
|
} else {
|
|
alert(resp.message || 'Gagal assign project')
|
|
}
|
|
},
|
|
async removeProject(project) {
|
|
if (!project || !project.mcu_id) return
|
|
if (!this.isEditMode) {
|
|
this.pendingProjects = this.pendingProjects.filter(p => p.mcu_id != project.mcu_id)
|
|
return
|
|
}
|
|
if (!this.username) return
|
|
let resp = await this.$store.dispatch('dashboard_user/remove_project', {
|
|
username: this.username,
|
|
mcu_id: project.mcu_id
|
|
})
|
|
if (resp.status == 'OK') {
|
|
this.refreshList()
|
|
} else {
|
|
alert(resp.message || 'Gagal remove project')
|
|
}
|
|
},
|
|
newForm() {
|
|
this.$store.commit('dashboard_user/update_selected_user', {})
|
|
this.username = ''
|
|
this.displayName = ''
|
|
this.password = ''
|
|
this.passwordTouched = false
|
|
this.selectedProject = null
|
|
this.projectKeyword = ''
|
|
this.pendingProjects = []
|
|
}
|
|
}
|
|
}
|
|
</script>
|