Refactor the Employee tab of profile view, per better patterns

learned some things from the Personal tab overhaul
This commit is contained in:
Lance Edgar 2021-10-05 08:25:33 -04:00
parent 6386b34516
commit e7fb1559f5
2 changed files with 161 additions and 225 deletions

View file

@ -452,20 +452,19 @@
</div> </div>
</%def> </%def>
<%def name="render_personal_tab_cards()">
${self.render_personal_name_card()}
${self.render_personal_address_card()}
${self.render_personal_phone_card()}
${self.render_personal_email_card()}
</%def>
<%def name="render_personal_tab_template()"> <%def name="render_personal_tab_template()">
<script type="text/x-template" id="personal-tab-template"> <script type="text/x-template" id="personal-tab-template">
<div style="display: flex; justify-content: space-between;"> <div style="display: flex; justify-content: space-between;">
<div style="flex-grow: 1; margin-right: 1rem;"> <div style="flex-grow: 1; margin-right: 1rem;">
${self.render_personal_tab_cards()}
${self.render_personal_name_card()}
${self.render_personal_address_card()}
${self.render_personal_phone_card()}
${self.render_personal_email_card()}
</div> </div>
<div> <div>
@ -670,7 +669,7 @@
<div style="flex-grow: 1;"> <div style="flex-grow: 1;">
<div v-if="employee.exists"> <div v-if="employee.uuid">
<b-field horizontal label="Employee ID"> <b-field horizontal label="Employee ID">
<div class="level"> <div class="level">
@ -721,22 +720,22 @@
</b-field> </b-field>
<b-field horizontal label="Employee Status"> <b-field horizontal label="Employee Status">
<span>{{ employee.current ? "current" : "former" }}</span> <span>{{ employee.status_display }}</span>
</b-field> </b-field>
<b-field horizontal label="Start Date"> <b-field horizontal label="Start Date">
<span>{{ employee.startDate }}</span> <span>{{ employee.start_date }}</span>
</b-field> </b-field>
<b-field horizontal label="End Date"> <b-field horizontal label="End Date">
<span>{{ employee.endDate }}</span> <span>{{ employee.end_date }}</span>
</b-field> </b-field>
<br /> <br />
<p><strong>Employee History</strong></p> <p><strong>Employee History</strong></p>
<br /> <br />
<b-table :data="employeeHistory.data"> <b-table :data="employeeHistory">
<template slot-scope="props"> <template slot-scope="props">
<b-table-column field="start_date" label="Start Date"> <b-table-column field="start_date" label="Start Date">
@ -761,7 +760,7 @@
</div> </div>
<p v-if="!employee.exists"> <p v-if="!employee.uuid">
${person} has never been an employee. ${person} has never been an employee.
</p> </p>
@ -774,7 +773,7 @@
<b-button v-if="!employee.current" <b-button v-if="!employee.current"
type="is-primary" type="is-primary"
@click="showStartEmployee()"> @click="startEmployeeInit()">
${person} is now an Employee ${person} is now an Employee
</b-button> </b-button>
@ -785,7 +784,7 @@
</b-button> </b-button>
<b-modal has-modal-card <b-modal has-modal-card
:active.sync="showStartEmployeeDialog"> :active.sync="startEmployeeShowDialog">
<div class="modal-card"> <div class="modal-card">
<header class="modal-card-head"> <header class="modal-card-head">
@ -802,7 +801,7 @@
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<b-button @click="showStartEmployeeDialog = false"> <b-button @click="startEmployeeShowDialog = false">
Cancel Cancel
</b-button> </b-button>
<once-button type="is-primary" <once-button type="is-primary"
@ -882,8 +881,8 @@
% endif % endif
% if request.has_perm('employees.view'): % if request.has_perm('employees.view'):
<b-button v-if="employee.viewURL" <b-button v-if="employee.view_url"
tag="a" :href="employee.viewURL"> tag="a" :href="employee.view_url">
View Employee View Employee
</b-button> </b-button>
% endif % endif
@ -902,24 +901,14 @@
:icon="employee.current ? 'check' : null"> :icon="employee.current ? 'check' : null">
<employee-tab :employee="employee" <employee-tab :employee="employee"
:employee-history="employeeHistory" :employee-history="employeeHistory"
@employee-updated="employeeUpdated"
@employee-history-updated="employeeHistoryUpdated"
@change-content-title="changeContentTitle"> @change-content-title="changeContentTitle">
</employee-tab> </employee-tab>
</b-tab-item> </b-tab-item>
</%def> </%def>
<%def name="render_profile_info_template()"> <%def name="render_user_tab()">
<script type="text/x-template" id="profile-info-template">
<div>
<b-tabs v-model="activeTab" type="is-boxed">
${self.render_personal_tab()}
${self.render_customer_tab()}
${self.render_member_tab()}
${self.render_employee_tab()}
<b-tab-item label="User" ${'icon="check" icon-pack="fas"' if person.users else ''|n}> <b-tab-item label="User" ${'icon="check" icon-pack="fas"' if person.users else ''|n}>
% if person.users: % if person.users:
<p>${person} is associated with <strong>${len(person.users)}</strong> user account(s)</p> <p>${person} is associated with <strong>${len(person.users)}</strong> user account(s)</p>
@ -976,7 +965,21 @@
<p>${person} has never been a user.</p> <p>${person} has never been a user.</p>
% endif % endif
</b-tab-item><!-- User --> </b-tab-item><!-- User -->
</%def>
<%def name="render_profile_tabs()">
${self.render_personal_tab()}
${self.render_customer_tab()}
${self.render_member_tab()}
${self.render_employee_tab()}
${self.render_user_tab()}
</%def>
<%def name="render_profile_info_template()">
<script type="text/x-template" id="profile-info-template">
<div>
<b-tabs v-model="activeTab" type="is-boxed">
${self.render_profile_tabs()}
</b-tabs> </b-tabs>
</div> </div>
</script> </script>
@ -1362,37 +1365,12 @@
</script> </script>
</%def> </%def>
<%def name="set_employee_data()">
<script type="text/javascript">
let EmployeeData = {
exists: ${json.dumps(bool(employee))|n},
viewURL: ${json.dumps(employee_view_url)|n},
current: ${json.dumps(bool(employee and employee.status == enum.EMPLOYEE_STATUS_CURRENT))|n},
startDate: ${json.dumps(six.text_type(employee_history.start_date) if employee_history else None)|n},
endDate: ${json.dumps(six.text_type(employee_history.end_date) if employee_history and employee_history.end_date else None)|n},
id: ${json.dumps(employee.id if employee else None)|n},
}
let EmployeeHistoryData = {
data: ${json.dumps(employee_history_data)|n},
}
</script>
</%def>
<%def name="declare_employee_tab_vars()"> <%def name="declare_employee_tab_vars()">
<script type="text/javascript"> <script type="text/javascript">
let EmployeeTab = { let EmployeeTabData = {
template: '#employee-tab-template',
props: { startEmployeeShowDialog: false,
employee: Object,
employeeHistory: Object,
},
data() {
return {
showStartEmployeeDialog: false,
employeeID: null, employeeID: null,
employeeStartDate: null, employeeStartDate: null,
showStopEmployeeDialog: false, showStopEmployeeDialog: false,
@ -1409,10 +1387,14 @@
newEmployeeID: null, newEmployeeID: null,
updatingEmployeeID: false, updatingEmployeeID: false,
% endif % endif
## TODO: should find a better way to handle CSRF token
csrftoken: ${json.dumps(request.session.get_csrf_token() or request.session.new_csrf_token())|n},
} }
let EmployeeTab = {
template: '#employee-tab-template',
mixins: [SubmitMixin],
props: {
employee: Object,
employeeHistory: Object,
}, },
computed: { computed: {
@ -1452,26 +1434,11 @@
'employee_id': this.newEmployeeID, 'employee_id': this.newEmployeeID,
} }
let headers = { let that = this
## TODO: should find a better way to handle CSRF token this.submitData(url, params, function(response) {
'X-CSRF-TOKEN': this.csrftoken, that.$emit('employee-updated', response.data.employee)
} that.showEditEmployeeIDDialog = false
that.updatingEmployeeID = false
## TODO: should find a better way to handle CSRF token
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
if (data.success) {
this.employee.id = data.employee.id
this.showEditEmployeeIDDialog = false
this.updatingEmployeeID = false
} else {
this.$buefy.toast.open({
message: "Save failed: " + data.error,
type: 'is-danger',
duration: 4000, // 4 seconds
})
}
}, response => {
alert("Unexpected error occurred!")
}) })
}, },
@ -1479,13 +1446,12 @@
% if request.has_perm('people_profile.toggle_employee'): % if request.has_perm('people_profile.toggle_employee'):
showStartEmployee() { startEmployeeInit() {
this.employeeID = this.employee.id this.employeeID = this.employee.id || null
this.showStartEmployeeDialog = true this.startEmployeeShowDialog = true
}, },
startEmployee() { startEmployee() {
let url = '${url('people.profile_start_employee', uuid=person.uuid)}' let url = '${url('people.profile_start_employee', uuid=person.uuid)}'
let params = { let params = {
@ -1493,44 +1459,26 @@
start_date: this.employeeStartDate, start_date: this.employeeStartDate,
} }
let headers = { let that = this
## TODO: should find a better way to handle CSRF token this.submitData(url, params, function(response) {
'X-CSRF-TOKEN': this.csrftoken, that.startEmployeeSuccess(response.data)
}
## TODO: should find a better way to handle CSRF token
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
if (data.success) {
this.startEmployeeSuccess(data)
} else {
this.$buefy.toast.open({
message: "Save failed: " + data.error,
type: 'is-danger',
duration: 4000, // 4 seconds
})
}
}, response => {
alert("Unexpected error occurred!")
}) })
}, },
startEmployeeSuccess(data) { startEmployeeSuccess(data) {
this.employee.exists = true this.$emit('employee-updated', data.employee)
this.employee.id = data.employee_id this.$emit('employee-history-updated', data.employee_history_data)
this.employee.viewURL = data.employee_view_url
this.employee.current = true
this.employee.startDate = data.start_date
this.employee.endDate = null
this.employeeHistory.data = data.employee_history_data
// this.customerNumber = data.customer_number
this.employeeEndDate = null
this.$emit('change-content-title', data.dynamic_content_title) this.$emit('change-content-title', data.dynamic_content_title)
// this.posTabStale = true
this.showStartEmployeeDialog = false // let derived component do more here if needed
this.startEmployeeSuccessExtra(data)
this.startEmployeeShowDialog = false
}, },
endEmployee() { startEmployeeSuccessExtra(data) {},
endEmployee() {
let url = '${url('people.profile_end_employee', uuid=person.uuid)}' let url = '${url('people.profile_end_employee', uuid=person.uuid)}'
let params = { let params = {
@ -1538,38 +1486,25 @@
revoke_access: this.employeeRevokeAccess, revoke_access: this.employeeRevokeAccess,
} }
let headers = { let that = this
## TODO: should find a better way to handle CSRF token this.submitData(url, params, function(response) {
'X-CSRF-TOKEN': this.csrftoken, that.endEmployeeSuccess(response.data)
}
## TODO: should find a better way to handle CSRF token
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
if (data.success) {
this.endEmployeeSuccess(data)
} else {
this.$buefy.toast.open({
message: "Save failed: " + data.error,
type: 'is-danger',
duration: 4000, // 4 seconds
})
}
}, response => {
alert("Unexpected error occurred!")
}) })
}, },
endEmployeeSuccess(data) { endEmployeeSuccess(data) {
this.employee.current = false this.$emit('employee-updated', data.employee)
this.employee.endDate = data.end_date this.$emit('employee-history-updated', data.employee_history_data)
this.employeeHistory.data = data.employee_history_data
this.employeeStartDate = null
this.$emit('change-content-title', data.dynamic_content_title) this.$emit('change-content-title', data.dynamic_content_title)
// this.memberTabStale = true
// this.posTabStale = true // let derived component do more here if needed
this.startEmployeeSuccessExtra(data)
this.showStopEmployeeDialog = false this.showStopEmployeeDialog = false
}, },
endEmployeeSuccessExtra(data) {},
% endif % endif
% if request.has_perm('people_profile.edit_employee_history'): % if request.has_perm('people_profile.edit_employee_history'):
@ -1583,7 +1518,6 @@
}, },
saveEmployeeHistory() { saveEmployeeHistory() {
let url = '${url('people.profile_edit_employee_history', uuid=person.uuid)}' let url = '${url('people.profile_edit_employee_history', uuid=person.uuid)}'
let params = { let params = {
@ -1592,19 +1526,11 @@
end_date: this.employeeHistoryEndDate, end_date: this.employeeHistoryEndDate,
} }
let headers = { let that = this
## TODO: should find a better way to handle CSRF token this.submitData(url, params, function(response) {
'X-CSRF-TOKEN': this.csrftoken, that.$emit('employee-updated', response.data.employee)
} that.$emit('employee-history-updated', response.data.employee_history_data)
that.showEditEmployeeHistoryDialog = false
## TODO: should find a better way to handle CSRF token
this.$http.post(url, params, {headers: headers}).then(({ data }) => {
if (data.success) {
this.employee.startDate = data.start_date
this.employee.endDate = data.end_date
this.employeeHistory.data = data.employee_history_data
}
this.showEditEmployeeHistoryDialog = false
}) })
}, },
@ -1619,6 +1545,7 @@
${self.declare_employee_tab_vars()} ${self.declare_employee_tab_vars()}
<script type="text/javascript"> <script type="text/javascript">
EmployeeTab.data = function() { return EmployeeTabData }
Vue.component('employee-tab', EmployeeTab) Vue.component('employee-tab', EmployeeTab)
</script> </script>
@ -1633,8 +1560,8 @@
customers: ${json.dumps(customers_data)|n}, customers: ${json.dumps(customers_data)|n},
member: null, // TODO member: null, // TODO
members: ${json.dumps(members_data)|n}, members: ${json.dumps(members_data)|n},
employee: EmployeeData, employee: ${json.dumps(employee_data)|n},
employeeHistory: EmployeeHistoryData, employeeHistory: ${json.dumps(employee_history_data)|n},
phoneTypeOptions: ${json.dumps(phone_type_options)|n}, phoneTypeOptions: ${json.dumps(phone_type_options)|n},
emailTypeOptions: ${json.dumps(email_type_options)|n}, emailTypeOptions: ${json.dumps(email_type_options)|n},
maxLengths: ${json.dumps(max_lengths)|n}, maxLengths: ${json.dumps(max_lengths)|n},
@ -1647,6 +1574,12 @@
personUpdated(person) { personUpdated(person) {
this.person = person this.person = person
}, },
employeeUpdated(employee) {
this.employee = employee
},
employeeHistoryUpdated(employeeHistory) {
this.employeeHistory = employeeHistory
},
changeContentTitle(newTitle) { changeContentTitle(newTitle) {
this.$emit('change-content-title', newTitle) this.$emit('change-content-title', newTitle)
}, },
@ -1722,7 +1655,6 @@
<%def name="make_this_page_component()"> <%def name="make_this_page_component()">
${parent.make_this_page_component()} ${parent.make_this_page_component()}
${self.make_personal_tab_component()} ${self.make_personal_tab_component()}
${self.set_employee_data()}
${self.make_employee_tab_component()} ${self.make_employee_tab_component()}
${self.make_profile_info_component()} ${self.make_profile_info_component()}
</%def> </%def>

View file

@ -441,6 +441,7 @@ class PersonView(MasterView):
'customers_data': self.get_context_customers(person), 'customers_data': self.get_context_customers(person),
'members_data': self.get_context_members(person), 'members_data': self.get_context_members(person),
'employee': employee, 'employee': employee,
'employee_data': self.get_context_employee(employee) if employee else {},
'employee_view_url': self.request.route_url('employees.view', uuid=employee.uuid) if employee else None, 'employee_view_url': self.request.route_url('employees.view', uuid=employee.uuid) if employee else None,
'employee_history': employee.get_current_history() if employee else None, 'employee_history': employee.get_current_history() if employee else None,
'employee_history_data': self.get_context_employee_history(employee), 'employee_history_data': self.get_context_employee_history(employee),
@ -581,6 +582,7 @@ class PersonView(MasterView):
app = self.get_rattail_app() app = self.get_rattail_app()
handler = app.get_employment_handler() handler = app.get_employment_handler()
context = handler.get_context_employee(employee) context = handler.get_context_employee(employee)
context['view_url'] = self.request.route_url('employees.view', uuid=employee.uuid)
return context return context
def get_context_employee_history(self, employee): def get_context_employee_history(self, employee):
@ -884,6 +886,7 @@ class PersonView(MasterView):
start_date = datetime.datetime.strptime(data['start_date'], '%Y-%m-%d').date() start_date = datetime.datetime.strptime(data['start_date'], '%Y-%m-%d').date()
employee = handler.begin_employment(person, start_date, employee = handler.begin_employment(person, start_date,
employee_id=data['id']) employee_id=data['id'])
self.Session.flush()
return self.profile_start_employee_result(employee, start_date) return self.profile_start_employee_result(employee, start_date)
def profile_start_employee_result(self, employee, start_date): def profile_start_employee_result(self, employee, start_date):
@ -912,6 +915,7 @@ class PersonView(MasterView):
employee = handler.get_employee(person) employee = handler.get_employee(person)
handler.end_employment(employee, end_date, handler.end_employment(employee, end_date,
revoke_access=data.get('revoke_access')) revoke_access=data.get('revoke_access'))
self.Session.flush()
return self.profile_end_employee_result(employee, end_date) return self.profile_end_employee_result(employee, end_date)
def profile_end_employee_result(self, employee, end_date): def profile_end_employee_result(self, employee, end_date):
@ -948,9 +952,9 @@ class PersonView(MasterView):
self.Session.flush() self.Session.flush()
current_history = employee.get_current_history() current_history = employee.get_current_history()
return { return {
'success': True, 'success': True,
'employee': self.get_context_employee(employee),
'start_date': six.text_type(current_history.start_date), 'start_date': six.text_type(current_history.start_date),
'end_date': six.text_type(current_history.end_date or ''), 'end_date': six.text_type(current_history.end_date or ''),
'employee_history_data': self.get_context_employee_history(employee), 'employee_history_data': self.get_context_employee_history(employee),