Add support for Notes tab in profile view
This commit is contained in:
parent
105dab7a3d
commit
d77de76c97
|
@ -1090,6 +1090,153 @@
|
||||||
</b-tab-item>
|
</b-tab-item>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_notes_tab_template()">
|
||||||
|
<script type="text/x-template" id="notes-tab-template">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.add_note'):
|
||||||
|
<b-button type="is-primary"
|
||||||
|
class="control"
|
||||||
|
@click="noteNew()"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="plus">
|
||||||
|
Add Note
|
||||||
|
</b-button>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<b-table :data="notes">
|
||||||
|
|
||||||
|
<b-table-column field="note_type"
|
||||||
|
label="Type"
|
||||||
|
v-slot="props">
|
||||||
|
{{ props.row.note_type_display }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="subject"
|
||||||
|
label="Subject"
|
||||||
|
v-slot="props">
|
||||||
|
{{ props.row.subject }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="text"
|
||||||
|
label="Text"
|
||||||
|
v-slot="props">
|
||||||
|
{{ props.row.text }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="created"
|
||||||
|
label="Created"
|
||||||
|
v-slot="props">
|
||||||
|
<span v-html="props.row.created_display"></span>
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
<b-table-column field="created_by"
|
||||||
|
label="Created By"
|
||||||
|
v-slot="props">
|
||||||
|
{{ props.row.created_by_display }}
|
||||||
|
</b-table-column>
|
||||||
|
|
||||||
|
% if request.has_any_perm('people_profile.edit_note', 'people_profile.delete_note'):
|
||||||
|
<b-table-column label="Actions"
|
||||||
|
v-slot="props">
|
||||||
|
% if request.has_perm('people_profile.edit_note'):
|
||||||
|
<a href="#" @click.prevent="noteEdit(props.row)">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
% endif
|
||||||
|
% if request.has_perm('people_profile.delete_note'):
|
||||||
|
<a href="#" @click.prevent="noteDelete(props.row)"
|
||||||
|
class="has-text-danger">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
|
% endif
|
||||||
|
</b-table-column>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</b-table>
|
||||||
|
|
||||||
|
<b-modal :active.sync="noteShowDialog"
|
||||||
|
has-modal-card>
|
||||||
|
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">
|
||||||
|
{{ noteDialogTitle }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
|
||||||
|
<b-field label="Type"
|
||||||
|
:type="!noteDeleting && !noteType ? 'is-danger' : null">
|
||||||
|
<b-select v-model="noteType"
|
||||||
|
:disabled="noteUUID">
|
||||||
|
<option v-for="option in noteTypeOptions"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Subject">
|
||||||
|
<b-input v-model.trim="noteSubject"
|
||||||
|
:disabled="noteDeleting">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Text">
|
||||||
|
<b-input v-model.trim="noteText"
|
||||||
|
type="textarea"
|
||||||
|
:disabled="noteDeleting">
|
||||||
|
</b-input>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-notification v-if="noteDeleting"
|
||||||
|
type="is-danger"
|
||||||
|
:closable="false">
|
||||||
|
Are you sure you wish to delete this note?
|
||||||
|
</b-notification>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button :type="noteDeleting ? 'is-danger' : 'is-primary'"
|
||||||
|
@click="noteSave()"
|
||||||
|
:disabled="noteSaving || (!noteDeleting && !noteType)"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save">
|
||||||
|
{{ noteSaving ? "Working, please wait..." : noteSaveText }}
|
||||||
|
</b-button>
|
||||||
|
<b-button @click="noteShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_notes_tab()">
|
||||||
|
<b-tab-item label="Notes"
|
||||||
|
value="notes"
|
||||||
|
icon-pack="fas"
|
||||||
|
:icon="notes.length ? 'check' : null">
|
||||||
|
|
||||||
|
<notes-tab :notes="notes"
|
||||||
|
:note-type-options="noteTypeOptions"
|
||||||
|
@new-notes-data="newNotesData">
|
||||||
|
</notes-tab>
|
||||||
|
|
||||||
|
</b-tab-item>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="render_user_tab()">
|
<%def name="render_user_tab()">
|
||||||
<b-tab-item label="User"
|
<b-tab-item label="User"
|
||||||
value="user"
|
value="user"
|
||||||
|
@ -1154,6 +1301,7 @@
|
||||||
${self.render_shopper_tab()}
|
${self.render_shopper_tab()}
|
||||||
% endif
|
% endif
|
||||||
${self.render_employee_tab()}
|
${self.render_employee_tab()}
|
||||||
|
${self.render_notes_tab()}
|
||||||
${self.render_user_tab()}
|
${self.render_user_tab()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -1271,6 +1419,7 @@
|
||||||
${parent.render_this_page_template()}
|
${parent.render_this_page_template()}
|
||||||
${self.render_personal_tab_template()}
|
${self.render_personal_tab_template()}
|
||||||
${self.render_employee_tab_template()}
|
${self.render_employee_tab_template()}
|
||||||
|
${self.render_notes_tab_template()}
|
||||||
${self.render_profile_info_template()}
|
${self.render_profile_info_template()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -1833,6 +1982,136 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="declare_notes_tab_vars()">
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
let NotesTabData = {
|
||||||
|
noteShowDialog: false,
|
||||||
|
noteUUID: null,
|
||||||
|
noteType: null,
|
||||||
|
noteSubject: null,
|
||||||
|
noteText: null,
|
||||||
|
noteDeleting: false,
|
||||||
|
noteSaving: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
let NotesTab = {
|
||||||
|
template: '#notes-tab-template',
|
||||||
|
mixins: [SimpleRequestMixin],
|
||||||
|
props: {
|
||||||
|
notes: Array,
|
||||||
|
noteTypeOptions: Array,
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
noteDialogTitle() {
|
||||||
|
if (this.noteUUID) {
|
||||||
|
if (this.noteDeleting) {
|
||||||
|
return "Delete Note"
|
||||||
|
}
|
||||||
|
return "Edit Note"
|
||||||
|
}
|
||||||
|
return "New Note"
|
||||||
|
},
|
||||||
|
|
||||||
|
noteSaveText() {
|
||||||
|
if (this.noteDeleting) {
|
||||||
|
return "Delete Note"
|
||||||
|
}
|
||||||
|
return "Save Note"
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.add_note'):
|
||||||
|
|
||||||
|
noteNew() {
|
||||||
|
this.noteUUID = null
|
||||||
|
this.noteType = null
|
||||||
|
this.noteSubject = null
|
||||||
|
this.noteText = null
|
||||||
|
this.noteDeleting = false
|
||||||
|
this.noteShowDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.edit_note'):
|
||||||
|
|
||||||
|
noteEdit(note) {
|
||||||
|
this.noteUUID = note.uuid
|
||||||
|
this.noteType = note.note_type
|
||||||
|
this.noteSubject = note.subject
|
||||||
|
this.noteText = note.text
|
||||||
|
this.noteDeleting = false
|
||||||
|
this.noteShowDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.delete_note'):
|
||||||
|
|
||||||
|
noteDelete(note) {
|
||||||
|
this.noteUUID = note.uuid
|
||||||
|
this.noteType = note.note_type
|
||||||
|
this.noteSubject = note.subject
|
||||||
|
this.noteText = note.text
|
||||||
|
this.noteDeleting = true
|
||||||
|
this.noteShowDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.has_any_perm('people_profile.add_note', 'people_profile.edit_note', 'people_profile.delete_note'):
|
||||||
|
|
||||||
|
noteSave() {
|
||||||
|
this.noteSaving = true
|
||||||
|
|
||||||
|
let url = null
|
||||||
|
if (!this.noteUUID) {
|
||||||
|
url = '${master.get_action_url('profile_add_note', instance)}'
|
||||||
|
} else if (this.noteDeleting) {
|
||||||
|
url = '${master.get_action_url('profile_delete_note', instance)}'
|
||||||
|
} else {
|
||||||
|
url = '${master.get_action_url('profile_edit_note', instance)}'
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
uuid: this.noteUUID,
|
||||||
|
note_type: this.noteType,
|
||||||
|
note_subject: this.noteSubject,
|
||||||
|
note_text: this.noteText,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.simplePOST(url, params, response => {
|
||||||
|
this.$emit('new-notes-data', response.data.notes)
|
||||||
|
this.noteSaving = false
|
||||||
|
this.noteShowDialog = false
|
||||||
|
}, response => {
|
||||||
|
this.notesSaving = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_notes_tab_component()">
|
||||||
|
${self.declare_notes_tab_vars()}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
NotesTab.data = function() { return NotesTabData }
|
||||||
|
Vue.component('notes-tab', NotesTab)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="declare_profile_info_vars()">
|
<%def name="declare_profile_info_vars()">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
@ -1847,6 +2126,8 @@
|
||||||
members: ${json.dumps(members_data)|n},
|
members: ${json.dumps(members_data)|n},
|
||||||
employee: ${json.dumps(employee_data)|n},
|
employee: ${json.dumps(employee_data)|n},
|
||||||
employeeHistory: ${json.dumps(employee_history_data)|n},
|
employeeHistory: ${json.dumps(employee_history_data)|n},
|
||||||
|
notes: ${json.dumps(notes_data)|n},
|
||||||
|
noteTypeOptions: ${json.dumps(note_type_options)|n},
|
||||||
users: ${json.dumps(users_data)|n},
|
users: ${json.dumps(users_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},
|
||||||
|
@ -1876,6 +2157,10 @@
|
||||||
computed: {},
|
computed: {},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
newNotesData(notes) {
|
||||||
|
this.notes = notes
|
||||||
|
},
|
||||||
|
|
||||||
personUpdated(person) {
|
personUpdated(person) {
|
||||||
this.person = person
|
this.person = person
|
||||||
},
|
},
|
||||||
|
@ -2001,6 +2286,7 @@
|
||||||
${parent.make_this_page_component()}
|
${parent.make_this_page_component()}
|
||||||
${self.make_personal_tab_component()}
|
${self.make_personal_tab_component()}
|
||||||
${self.make_employee_tab_component()}
|
${self.make_employee_tab_component()}
|
||||||
|
${self.make_notes_tab_component()}
|
||||||
${self.make_profile_info_component()}
|
${self.make_profile_info_component()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@ class PersonView(MasterView):
|
||||||
has_versions = True
|
has_versions = True
|
||||||
bulk_deletable = True
|
bulk_deletable = True
|
||||||
is_contact = True
|
is_contact = True
|
||||||
manage_notes_from_profile_view = False
|
|
||||||
supports_autocomplete = True
|
supports_autocomplete = True
|
||||||
configurable = True
|
configurable = True
|
||||||
|
|
||||||
|
@ -460,6 +459,8 @@ class PersonView(MasterView):
|
||||||
'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),
|
||||||
|
'notes_data': self.get_context_notes(person),
|
||||||
|
'note_type_options': self.get_note_type_options(),
|
||||||
'users_data': self.get_context_users(person),
|
'users_data': self.get_context_users(person),
|
||||||
'dynamic_content_title': self.get_context_content_title(person),
|
'dynamic_content_title': self.get_context_content_title(person),
|
||||||
}
|
}
|
||||||
|
@ -752,6 +753,29 @@ class PersonView(MasterView):
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_context_notes(self, person):
|
||||||
|
data = []
|
||||||
|
notes = sorted(person.notes, key=lambda n: n.created, reverse=True)
|
||||||
|
for note in notes:
|
||||||
|
data.append(self.get_context_note(note))
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_context_note(self, note):
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
return {
|
||||||
|
'uuid': note.uuid,
|
||||||
|
'note_type': note.type,
|
||||||
|
'note_type_display': self.enum.PERSON_NOTE_TYPE.get(note.type, note.type),
|
||||||
|
'subject': note.subject,
|
||||||
|
'text': note.text,
|
||||||
|
'created_display': raw_datetime(self.rattail_config, note.created),
|
||||||
|
'created_by_display': str(note.created_by),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_note_type_options(self):
|
||||||
|
return [{'value': k, 'label': v}
|
||||||
|
for k, v in self.enum.PERSON_NOTE_TYPE.items()]
|
||||||
|
|
||||||
def get_context_users(self, person):
|
def get_context_users(self, person):
|
||||||
data = []
|
data = []
|
||||||
users = person.users
|
users = person.users
|
||||||
|
@ -1385,6 +1409,8 @@ class PersonView(MasterView):
|
||||||
if mode == 'create':
|
if mode == 'create':
|
||||||
del schema['uuid']
|
del schema['uuid']
|
||||||
form = forms.Form(schema=schema, request=self.request)
|
form = forms.Form(schema=schema, request=self.request)
|
||||||
|
if mode != 'delete':
|
||||||
|
form.set_validator('note_type', colander.OneOf(self.enum.PERSON_NOTE_TYPE))
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def profile_add_note(self):
|
def profile_add_note(self):
|
||||||
|
@ -1406,11 +1432,15 @@ class PersonView(MasterView):
|
||||||
person.notes.append(note)
|
person.notes.append(note)
|
||||||
return note
|
return note
|
||||||
|
|
||||||
def profile_add_note_success(self, note):
|
def profile_add_note_success(self, note, person=None):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return {
|
||||||
|
'notes': self.get_context_notes(person or note.person),
|
||||||
|
}
|
||||||
|
|
||||||
def profile_add_note_failure(self, person, form):
|
def profile_add_note_failure(self, person, form):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return {
|
||||||
|
'error': str(form.make_deform_form().error),
|
||||||
|
}
|
||||||
|
|
||||||
def profile_edit_note(self):
|
def profile_edit_note(self):
|
||||||
person = self.get_instance()
|
person = self.get_instance()
|
||||||
|
@ -1429,10 +1459,10 @@ class PersonView(MasterView):
|
||||||
return note
|
return note
|
||||||
|
|
||||||
def profile_edit_note_success(self, note):
|
def profile_edit_note_success(self, note):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return self.profile_add_note_success(note)
|
||||||
|
|
||||||
def profile_edit_note_failure(self, person, form):
|
def profile_edit_note_failure(self, person, form):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return self.profile_add_note_failure(person, form)
|
||||||
|
|
||||||
def profile_delete_note(self):
|
def profile_delete_note(self):
|
||||||
person = self.get_instance()
|
person = self.get_instance()
|
||||||
|
@ -1449,10 +1479,10 @@ class PersonView(MasterView):
|
||||||
self.Session.delete(note)
|
self.Session.delete(note)
|
||||||
|
|
||||||
def profile_delete_note_success(self, person):
|
def profile_delete_note_success(self, person):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return self.profile_add_note_success(None, person=person)
|
||||||
|
|
||||||
def profile_delete_note_failure(self, person, form):
|
def profile_delete_note_failure(self, person, form):
|
||||||
return self.redirect(self.get_action_url('view_profile', person))
|
return self.profile_add_note_failure(person, form)
|
||||||
|
|
||||||
def make_user(self):
|
def make_user(self):
|
||||||
uuid = self.request.POST['person_uuid']
|
uuid = self.request.POST['person_uuid']
|
||||||
|
@ -1657,32 +1687,41 @@ class PersonView(MasterView):
|
||||||
permission='people_profile.view_versions',
|
permission='people_profile.view_versions',
|
||||||
renderer='json')
|
renderer='json')
|
||||||
|
|
||||||
# manage notes from profile view
|
# profile - add note
|
||||||
if cls.manage_notes_from_profile_view:
|
config.add_tailbone_permission('people_profile',
|
||||||
|
'people_profile.add_note',
|
||||||
# add note
|
"Add new Note records")
|
||||||
config.add_tailbone_permission('people_profile', 'people_profile.add_note',
|
config.add_route(f'{route_prefix}.profile_add_note',
|
||||||
"Add new {} Note records".format(model_title))
|
f'{instance_url_prefix}/profile/new-note',
|
||||||
config.add_route('{}.profile_add_note'.format(route_prefix), '{}/{{{}}}/profile/new-note'.format(url_prefix, model_key),
|
|
||||||
request_method='POST')
|
request_method='POST')
|
||||||
config.add_view(cls, attr='profile_add_note', route_name='{}.profile_add_note'.format(route_prefix),
|
config.add_view(cls, attr='profile_add_note',
|
||||||
permission='people_profile.add_note')
|
route_name=f'{route_prefix}.profile_add_note',
|
||||||
|
permission='people_profile.add_note',
|
||||||
|
renderer='json')
|
||||||
|
|
||||||
# edit note
|
# profile - edit note
|
||||||
config.add_tailbone_permission('people_profile', 'people_profile.edit_note',
|
config.add_tailbone_permission('people_profile',
|
||||||
"Edit {} Note records".format(model_title))
|
'people_profile.edit_note',
|
||||||
config.add_route('{}.profile_edit_note'.format(route_prefix), '{}/{{{}}}/profile/edit-note'.format(url_prefix, model_key),
|
"Edit Note records")
|
||||||
|
config.add_route(f'{route_prefix}.profile_edit_note',
|
||||||
|
f'{instance_url_prefix}/profile/edit-note',
|
||||||
request_method='POST')
|
request_method='POST')
|
||||||
config.add_view(cls, attr='profile_edit_note', route_name='{}.profile_edit_note'.format(route_prefix),
|
config.add_view(cls, attr='profile_edit_note',
|
||||||
permission='people_profile.edit_note')
|
route_name=f'{route_prefix}.profile_edit_note',
|
||||||
|
permission='people_profile.edit_note',
|
||||||
|
renderer='json')
|
||||||
|
|
||||||
# delete note
|
# profile - delete note
|
||||||
config.add_tailbone_permission('people_profile', 'people_profile.delete_note',
|
config.add_tailbone_permission('people_profile',
|
||||||
"Delete {} Note records".format(model_title))
|
'people_profile.delete_note',
|
||||||
config.add_route('{}.profile_delete_note'.format(route_prefix), '{}/{{{}}}/profile/delete-note'.format(url_prefix, model_key),
|
"Delete Note records")
|
||||||
|
config.add_route(f'{route_prefix}.profile_delete_note',
|
||||||
|
f'{instance_url_prefix}/profile/delete-note',
|
||||||
request_method='POST')
|
request_method='POST')
|
||||||
config.add_view(cls, attr='profile_delete_note', route_name='{}.profile_delete_note'.format(route_prefix),
|
config.add_view(cls, attr='profile_delete_note',
|
||||||
permission='people_profile.delete_note')
|
route_name=f'{route_prefix}.profile_delete_note',
|
||||||
|
permission='people_profile.delete_note',
|
||||||
|
renderer='json')
|
||||||
|
|
||||||
# make user for person
|
# make user for person
|
||||||
config.add_route('{}.make_user'.format(route_prefix), '{}/make-user'.format(url_prefix),
|
config.add_route('{}.make_user'.format(route_prefix), '{}/make-user'.format(url_prefix),
|
||||||
|
|
Loading…
Reference in a new issue