Refactor the Employee tab of profile view, per better patterns
learned some things from the Personal tab overhaul
This commit is contained in:
parent
6386b34516
commit
e7fb1559f5
|
@ -452,20 +452,19 @@
|
|||
</div>
|
||||
</%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()">
|
||||
<script type="text/x-template" id="personal-tab-template">
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
|
||||
<div style="flex-grow: 1; margin-right: 1rem;">
|
||||
|
||||
${self.render_personal_name_card()}
|
||||
|
||||
${self.render_personal_address_card()}
|
||||
|
||||
${self.render_personal_phone_card()}
|
||||
|
||||
${self.render_personal_email_card()}
|
||||
|
||||
${self.render_personal_tab_cards()}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -670,7 +669,7 @@
|
|||
|
||||
<div style="flex-grow: 1;">
|
||||
|
||||
<div v-if="employee.exists">
|
||||
<div v-if="employee.uuid">
|
||||
|
||||
<b-field horizontal label="Employee ID">
|
||||
<div class="level">
|
||||
|
@ -721,22 +720,22 @@
|
|||
</b-field>
|
||||
|
||||
<b-field horizontal label="Employee Status">
|
||||
<span>{{ employee.current ? "current" : "former" }}</span>
|
||||
<span>{{ employee.status_display }}</span>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Start Date">
|
||||
<span>{{ employee.startDate }}</span>
|
||||
<span>{{ employee.start_date }}</span>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="End Date">
|
||||
<span>{{ employee.endDate }}</span>
|
||||
<span>{{ employee.end_date }}</span>
|
||||
</b-field>
|
||||
|
||||
<br />
|
||||
<p><strong>Employee History</strong></p>
|
||||
<br />
|
||||
|
||||
<b-table :data="employeeHistory.data">
|
||||
<b-table :data="employeeHistory">
|
||||
<template slot-scope="props">
|
||||
|
||||
<b-table-column field="start_date" label="Start Date">
|
||||
|
@ -761,7 +760,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<p v-if="!employee.exists">
|
||||
<p v-if="!employee.uuid">
|
||||
${person} has never been an employee.
|
||||
</p>
|
||||
|
||||
|
@ -774,7 +773,7 @@
|
|||
|
||||
<b-button v-if="!employee.current"
|
||||
type="is-primary"
|
||||
@click="showStartEmployee()">
|
||||
@click="startEmployeeInit()">
|
||||
${person} is now an Employee
|
||||
</b-button>
|
||||
|
||||
|
@ -785,7 +784,7 @@
|
|||
</b-button>
|
||||
|
||||
<b-modal has-modal-card
|
||||
:active.sync="showStartEmployeeDialog">
|
||||
:active.sync="startEmployeeShowDialog">
|
||||
<div class="modal-card">
|
||||
|
||||
<header class="modal-card-head">
|
||||
|
@ -802,7 +801,7 @@
|
|||
</section>
|
||||
|
||||
<footer class="modal-card-foot">
|
||||
<b-button @click="showStartEmployeeDialog = false">
|
||||
<b-button @click="startEmployeeShowDialog = false">
|
||||
Cancel
|
||||
</b-button>
|
||||
<once-button type="is-primary"
|
||||
|
@ -882,8 +881,8 @@
|
|||
% endif
|
||||
|
||||
% if request.has_perm('employees.view'):
|
||||
<b-button v-if="employee.viewURL"
|
||||
tag="a" :href="employee.viewURL">
|
||||
<b-button v-if="employee.view_url"
|
||||
tag="a" :href="employee.view_url">
|
||||
View Employee
|
||||
</b-button>
|
||||
% endif
|
||||
|
@ -902,81 +901,85 @@
|
|||
:icon="employee.current ? 'check' : null">
|
||||
<employee-tab :employee="employee"
|
||||
:employee-history="employeeHistory"
|
||||
@employee-updated="employeeUpdated"
|
||||
@employee-history-updated="employeeHistoryUpdated"
|
||||
@change-content-title="changeContentTitle">
|
||||
</employee-tab>
|
||||
</b-tab-item>
|
||||
</%def>
|
||||
|
||||
<%def name="render_user_tab()">
|
||||
<b-tab-item label="User" ${'icon="check" icon-pack="fas"' if person.users else ''|n}>
|
||||
% if person.users:
|
||||
<p>${person} is associated with <strong>${len(person.users)}</strong> user account(s)</p>
|
||||
<br />
|
||||
<div id="users-accordion">
|
||||
% for user in person.users:
|
||||
|
||||
<b-collapse class="panel"
|
||||
## TODO: what's up with aria-id here?
|
||||
## aria-id="contentIdForA11y2"
|
||||
>
|
||||
|
||||
<div
|
||||
slot="trigger"
|
||||
class="panel-heading"
|
||||
role="button"
|
||||
## TODO: what's up with aria-id here?
|
||||
## aria-controls="contentIdForA11y2"
|
||||
>
|
||||
<strong>${user.username}</strong>
|
||||
</div>
|
||||
|
||||
<div class="panel-block">
|
||||
|
||||
<div style="display: flex; justify-content: space-between; width: 100%;">
|
||||
|
||||
<div>
|
||||
|
||||
<div class="field-wrapper id">
|
||||
<div class="field-row">
|
||||
<label>Username</label>
|
||||
<div class="field">
|
||||
${user.username}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
% if request.has_perm('users.view'):
|
||||
${h.link_to("View User", url('users.view', uuid=user.uuid), class_='button')}
|
||||
% endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</b-collapse>
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
% else:
|
||||
<p>${person} has never been a user.</p>
|
||||
% endif
|
||||
</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_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}>
|
||||
% if person.users:
|
||||
<p>${person} is associated with <strong>${len(person.users)}</strong> user account(s)</p>
|
||||
<br />
|
||||
<div id="users-accordion">
|
||||
% for user in person.users:
|
||||
|
||||
<b-collapse class="panel"
|
||||
## TODO: what's up with aria-id here?
|
||||
## aria-id="contentIdForA11y2"
|
||||
>
|
||||
|
||||
<div
|
||||
slot="trigger"
|
||||
class="panel-heading"
|
||||
role="button"
|
||||
## TODO: what's up with aria-id here?
|
||||
## aria-controls="contentIdForA11y2"
|
||||
>
|
||||
<strong>${user.username}</strong>
|
||||
</div>
|
||||
|
||||
<div class="panel-block">
|
||||
|
||||
<div style="display: flex; justify-content: space-between; width: 100%;">
|
||||
|
||||
<div>
|
||||
|
||||
<div class="field-wrapper id">
|
||||
<div class="field-row">
|
||||
<label>Username</label>
|
||||
<div class="field">
|
||||
${user.username}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
% if request.has_perm('users.view'):
|
||||
${h.link_to("View User", url('users.view', uuid=user.uuid), class_='button')}
|
||||
% endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</b-collapse>
|
||||
% endfor
|
||||
</div>
|
||||
|
||||
% else:
|
||||
<p>${person} has never been a user.</p>
|
||||
% endif
|
||||
</b-tab-item><!-- User -->
|
||||
|
||||
${self.render_profile_tabs()}
|
||||
</b-tabs>
|
||||
</div>
|
||||
</script>
|
||||
|
@ -1362,58 +1365,37 @@
|
|||
</script>
|
||||
</%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()">
|
||||
<script type="text/javascript">
|
||||
|
||||
let EmployeeTabData = {
|
||||
|
||||
startEmployeeShowDialog: false,
|
||||
employeeID: null,
|
||||
employeeStartDate: null,
|
||||
showStopEmployeeDialog: false,
|
||||
employeeEndDate: null,
|
||||
employeeRevokeAccess: false,
|
||||
showEditEmployeeHistoryDialog: false,
|
||||
employeeHistoryUUID: null,
|
||||
employeeHistoryStartDate: null,
|
||||
employeeHistoryEndDate: null,
|
||||
employeeHistoryEndDateRequired: false,
|
||||
|
||||
% if request.has_perm('employees.edit'):
|
||||
showEditEmployeeIDDialog: false,
|
||||
newEmployeeID: null,
|
||||
updatingEmployeeID: false,
|
||||
% endif
|
||||
}
|
||||
|
||||
let EmployeeTab = {
|
||||
template: '#employee-tab-template',
|
||||
mixins: [SubmitMixin],
|
||||
props: {
|
||||
employee: Object,
|
||||
employeeHistory: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showStartEmployeeDialog: false,
|
||||
employeeID: null,
|
||||
employeeStartDate: null,
|
||||
showStopEmployeeDialog: false,
|
||||
employeeEndDate: null,
|
||||
employeeRevokeAccess: false,
|
||||
showEditEmployeeHistoryDialog: false,
|
||||
employeeHistoryUUID: null,
|
||||
employeeHistoryStartDate: null,
|
||||
employeeHistoryEndDate: null,
|
||||
employeeHistoryEndDateRequired: false,
|
||||
|
||||
% if request.has_perm('employees.edit'):
|
||||
showEditEmployeeIDDialog: false,
|
||||
newEmployeeID: null,
|
||||
updatingEmployeeID: false,
|
||||
% 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},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
|
@ -1452,26 +1434,11 @@
|
|||
'employee_id': this.newEmployeeID,
|
||||
}
|
||||
|
||||
let headers = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
'X-CSRF-TOKEN': this.csrftoken,
|
||||
}
|
||||
|
||||
## 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!")
|
||||
let that = this
|
||||
this.submitData(url, params, function(response) {
|
||||
that.$emit('employee-updated', response.data.employee)
|
||||
that.showEditEmployeeIDDialog = false
|
||||
that.updatingEmployeeID = false
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -1479,13 +1446,12 @@
|
|||
|
||||
% if request.has_perm('people_profile.toggle_employee'):
|
||||
|
||||
showStartEmployee() {
|
||||
this.employeeID = this.employee.id
|
||||
this.showStartEmployeeDialog = true
|
||||
startEmployeeInit() {
|
||||
this.employeeID = this.employee.id || null
|
||||
this.startEmployeeShowDialog = true
|
||||
},
|
||||
|
||||
startEmployee() {
|
||||
|
||||
let url = '${url('people.profile_start_employee', uuid=person.uuid)}'
|
||||
|
||||
let params = {
|
||||
|
@ -1493,44 +1459,26 @@
|
|||
start_date: this.employeeStartDate,
|
||||
}
|
||||
|
||||
let headers = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
'X-CSRF-TOKEN': this.csrftoken,
|
||||
}
|
||||
|
||||
## 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!")
|
||||
let that = this
|
||||
this.submitData(url, params, function(response) {
|
||||
that.startEmployeeSuccess(response.data)
|
||||
})
|
||||
},
|
||||
|
||||
startEmployeeSuccess(data) {
|
||||
this.employee.exists = true
|
||||
this.employee.id = data.employee_id
|
||||
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('employee-updated', data.employee)
|
||||
this.$emit('employee-history-updated', data.employee_history_data)
|
||||
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 params = {
|
||||
|
@ -1538,38 +1486,25 @@
|
|||
revoke_access: this.employeeRevokeAccess,
|
||||
}
|
||||
|
||||
let headers = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
'X-CSRF-TOKEN': this.csrftoken,
|
||||
}
|
||||
|
||||
## 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!")
|
||||
let that = this
|
||||
this.submitData(url, params, function(response) {
|
||||
that.endEmployeeSuccess(response.data)
|
||||
})
|
||||
},
|
||||
|
||||
endEmployeeSuccess(data) {
|
||||
this.employee.current = false
|
||||
this.employee.endDate = data.end_date
|
||||
this.employeeHistory.data = data.employee_history_data
|
||||
this.employeeStartDate = null
|
||||
this.$emit('employee-updated', data.employee)
|
||||
this.$emit('employee-history-updated', data.employee_history_data)
|
||||
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
|
||||
},
|
||||
|
||||
endEmployeeSuccessExtra(data) {},
|
||||
|
||||
% endif
|
||||
|
||||
% if request.has_perm('people_profile.edit_employee_history'):
|
||||
|
@ -1583,7 +1518,6 @@
|
|||
},
|
||||
|
||||
saveEmployeeHistory() {
|
||||
|
||||
let url = '${url('people.profile_edit_employee_history', uuid=person.uuid)}'
|
||||
|
||||
let params = {
|
||||
|
@ -1592,19 +1526,11 @@
|
|||
end_date: this.employeeHistoryEndDate,
|
||||
}
|
||||
|
||||
let headers = {
|
||||
## TODO: should find a better way to handle CSRF token
|
||||
'X-CSRF-TOKEN': this.csrftoken,
|
||||
}
|
||||
|
||||
## 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
|
||||
let that = this
|
||||
this.submitData(url, params, function(response) {
|
||||
that.$emit('employee-updated', response.data.employee)
|
||||
that.$emit('employee-history-updated', response.data.employee_history_data)
|
||||
that.showEditEmployeeHistoryDialog = false
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -1619,6 +1545,7 @@
|
|||
${self.declare_employee_tab_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
EmployeeTab.data = function() { return EmployeeTabData }
|
||||
Vue.component('employee-tab', EmployeeTab)
|
||||
|
||||
</script>
|
||||
|
@ -1633,8 +1560,8 @@
|
|||
customers: ${json.dumps(customers_data)|n},
|
||||
member: null, // TODO
|
||||
members: ${json.dumps(members_data)|n},
|
||||
employee: EmployeeData,
|
||||
employeeHistory: EmployeeHistoryData,
|
||||
employee: ${json.dumps(employee_data)|n},
|
||||
employeeHistory: ${json.dumps(employee_history_data)|n},
|
||||
phoneTypeOptions: ${json.dumps(phone_type_options)|n},
|
||||
emailTypeOptions: ${json.dumps(email_type_options)|n},
|
||||
maxLengths: ${json.dumps(max_lengths)|n},
|
||||
|
@ -1647,6 +1574,12 @@
|
|||
personUpdated(person) {
|
||||
this.person = person
|
||||
},
|
||||
employeeUpdated(employee) {
|
||||
this.employee = employee
|
||||
},
|
||||
employeeHistoryUpdated(employeeHistory) {
|
||||
this.employeeHistory = employeeHistory
|
||||
},
|
||||
changeContentTitle(newTitle) {
|
||||
this.$emit('change-content-title', newTitle)
|
||||
},
|
||||
|
@ -1722,7 +1655,6 @@
|
|||
<%def name="make_this_page_component()">
|
||||
${parent.make_this_page_component()}
|
||||
${self.make_personal_tab_component()}
|
||||
${self.set_employee_data()}
|
||||
${self.make_employee_tab_component()}
|
||||
${self.make_profile_info_component()}
|
||||
</%def>
|
||||
|
|
|
@ -441,6 +441,7 @@ class PersonView(MasterView):
|
|||
'customers_data': self.get_context_customers(person),
|
||||
'members_data': self.get_context_members(person),
|
||||
'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_history': employee.get_current_history() if employee else None,
|
||||
'employee_history_data': self.get_context_employee_history(employee),
|
||||
|
@ -581,6 +582,7 @@ class PersonView(MasterView):
|
|||
app = self.get_rattail_app()
|
||||
handler = app.get_employment_handler()
|
||||
context = handler.get_context_employee(employee)
|
||||
context['view_url'] = self.request.route_url('employees.view', uuid=employee.uuid)
|
||||
return context
|
||||
|
||||
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()
|
||||
employee = handler.begin_employment(person, start_date,
|
||||
employee_id=data['id'])
|
||||
self.Session.flush()
|
||||
return self.profile_start_employee_result(employee, start_date)
|
||||
|
||||
def profile_start_employee_result(self, employee, start_date):
|
||||
|
@ -912,6 +915,7 @@ class PersonView(MasterView):
|
|||
employee = handler.get_employee(person)
|
||||
handler.end_employment(employee, end_date,
|
||||
revoke_access=data.get('revoke_access'))
|
||||
self.Session.flush()
|
||||
return self.profile_end_employee_result(employee, end_date)
|
||||
|
||||
def profile_end_employee_result(self, employee, end_date):
|
||||
|
@ -948,9 +952,9 @@ class PersonView(MasterView):
|
|||
|
||||
self.Session.flush()
|
||||
current_history = employee.get_current_history()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'employee': self.get_context_employee(employee),
|
||||
'start_date': six.text_type(current_history.start_date),
|
||||
'end_date': six.text_type(current_history.end_date or ''),
|
||||
'employee_history_data': self.get_context_employee_history(employee),
|
||||
|
|
Loading…
Reference in a new issue