Portal UI enhancements: injury form improvements, coach ACL fixes, and color scheme updates

- Updated injury form fields and layout for portal consistency
- Removed team field from portal injury forms and made debug-only in internal views
- Fixed injury list color scheme (unverified=yellow, active=red) and added clickable links for treatment professionals
- Added comprehensive ACL permissions for coaches: res.partner, sports.patient.injury, mail.followers, mail.template
- Added record rule for coaches to access partner records
- Improved patient injury table in portal: role-based column visibility and navigation
- Enhanced status field color coding with Bootstrap text classes
- Added save/cancel buttons at top and bottom of edit forms
- Moved external notes field to end of injury detail form for better workflow
This commit is contained in:
Denis Durepos 2025-07-27 15:08:08 -04:00
parent a6742c16b0
commit 82266644ca
10 changed files with 296 additions and 199 deletions

View file

@ -153,3 +153,10 @@
- Keep this file updated as new TODOs are identified
- Reference relevant issue numbers when available
- Delete or check off items as they are completed
## MC's notes
- [ ]Change action button colors to Fit Crew color scheme (instead of the weird turquoise)
- [ ]Confirm followers added correctly when therapist adds player to team
- [x]Remove Team from injury detail (including from model - was not there before)
- [ ]Fix treatment professional selection in injury detail
- [ ]Add patient address add/edit to coach/therapist portal

View file

@ -51,12 +51,6 @@ class PatientInjuryPortal(CustomerPortal):
return_url = post.get('return_url', f'/my/player?player_id={patient_id}')
# Get patient's teams for the dropdown
teams = patient.team_ids
# Pre-selected team ID (when player is only on one team)
default_team_id = teams[0].id if len(teams) == 1 else None
# Check if user is a treatment professional
# Use request.env.user.has_group() directly to avoid security violations
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
@ -64,8 +58,6 @@ class PatientInjuryPortal(CustomerPortal):
values = {
'patient': patient,
'teams': teams, # Pass teams to the template for dropdown
'default_team_id': default_team_id, # Will be None if multiple teams
'return_url': return_url,
'page_name': 'report_injury',
'is_treatment_prof': is_treatment_prof, # Pass flag to template for conditional display
@ -90,31 +82,30 @@ class PatientInjuryPortal(CustomerPortal):
'error_message': str(e)
})
# Get the selected team
team_id = post.get('team_id')
if not team_id:
return request.redirect(f'/my/patient/injury/new?patient_id={patient_id}')
# Since team_id is no longer in the portal form, we'll use the patient's first team
# or None if the patient has multiple teams (let the model handle assignment)
patient_teams = patient.team_ids
team_id = patient_teams[0].id if len(patient_teams) == 1 else None
# Check if the current user is a treatment professional
is_treatment_prof = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
# Prepare values for injury creation
vals = {
'patient_id': int(patient_id),
'diagnosis': post.get('diagnosis'),
'external_notes': post.get('external_notes'),
# Set stage based on user role - treatment professionals create active injuries
'patient_id': patient.id,
'diagnosis': post.get('diagnosis', ''),
'injury_date': post.get('injury_date'),
'external_notes': post.get('external_notes', ''),
'stage': 'active' if is_treatment_prof else 'unverified',
'patient_name': patient.name,
# Store which team the injury was reported for
'team_id': int(team_id),
# Store parental consent value
'parental_consent': post.get('parental_consent', 'no'),
}
# Handle dates
if post.get('injury_date'):
vals['injury_date'] = post.get('injury_date')
# Only add team_id if we have a single team for the patient
if team_id:
vals['team_id'] = int(team_id)
# Handle optional fields
if post.get('parental_consent'):
vals['parental_consent'] = post.get('parental_consent')
if post.get('predicted_resolution_date'):
vals['predicted_resolution_date'] = post.get('predicted_resolution_date')
@ -227,24 +218,28 @@ class PatientInjuryPortal(CustomerPortal):
user = request.env.user
injury = request.env['sports.patient.injury'].browse(int(injury_id))
# Check if user has access to the team this injury is associated with
# Check if user has access to this injury based on patient's team ownership
if not injury.exists():
raise UserError(_('Injury not found.'))
# Get the team from the injury
team = injury.team_id
# Get the patient's teams
patient_teams = injury.patient_id.team_ids
if team:
# Check if user is part of the team staff
is_team_staff = team.staff_ids.filtered(lambda s: s.user_ids and user.id in s.user_ids.ids)
# Or if user is a medical professional with broader access
if patient_teams:
# Check if user is part of any of the patient's team staff
user_teams = request.env['sports.team.staff'].search([
('team_id', 'in', patient_teams.ids),
('user_ids', '=', user.id)
])
# Medical professionals might have specific access
# Use request.env.user.has_group() directly to avoid security violations
is_medical = request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional')
if not is_team_staff and not is_medical:
if not user_teams and not is_medical:
raise UserError(_('You do not have access to this injury.'))
else:
# If no team is specified, only medical professionals can access
# If patient has no teams, only medical professionals can access
# Use request.env.user.has_group() directly to avoid security violations
if not request.env.user.has_group('bemade_sports_clinic.group_portal_treatment_professional'):
raise UserError(_('You do not have access to this injury.'))
@ -278,14 +273,10 @@ class PatientInjuryPortal(CustomerPortal):
stage_selection = request.env['sports.patient.injury']._fields['stage'].selection
stages = [(k, v) for k, v in stage_selection]
# These were previously fetched from models that have been removed
body_locations = []
injury_types = []
# Get possible severity options if field exists
severity_options = []
if 'severity' in request.env['sports.patient.injury']._fields:
severity_options = request.env['sports.patient.injury']._fields['severity'].selection
# Get treatment professionals for the multi-select field
treatment_professionals = request.env['res.users'].search([
('groups_id', 'in', request.env.ref('bemade_sports_clinic.group_portal_treatment_professional').id)
])
# Get parental consent options if treatment professional
parental_consent_options = None
@ -295,9 +286,7 @@ class PatientInjuryPortal(CustomerPortal):
values = {
'injury': injury,
'stages': stages,
'body_locations': body_locations,
'injury_types': injury_types,
'severity_options': severity_options,
'treatment_professionals': treatment_professionals,
'parental_consent_options': parental_consent_options,
'return_url': return_url,
'is_treatment_prof': is_treatment_prof,
@ -339,18 +328,39 @@ class PatientInjuryPortal(CustomerPortal):
'external_notes': post.get('external_notes', injury.external_notes or ''),
})
# Handle injury date and N/A checkbox
if post.get('injury_date_na'):
vals['injury_date_na'] = True
vals['injury_date'] = False # Clear the date if N/A is checked
else:
vals['injury_date_na'] = False
if post.get('injury_date'):
vals['injury_date'] = post.get('injury_date')
# Handle resolution dates
if post.get('predicted_resolution_date'):
vals['predicted_resolution_date'] = post.get('predicted_resolution_date')
if post.get('resolution_date'):
vals['resolution_date'] = post.get('resolution_date')
# Handle treatment professionals (multi-select)
if post.get('treatment_professional_ids'):
# Convert to list if it's a single value
prof_ids = post.get('treatment_professional_ids')
if isinstance(prof_ids, str):
prof_ids = [prof_ids]
elif not isinstance(prof_ids, list):
prof_ids = [prof_ids]
# Convert to integers and set using Odoo's many2many syntax
prof_ids = [int(pid) for pid in prof_ids if pid]
vals['treatment_professional_ids'] = [(6, 0, prof_ids)]
# Fields only treatment professionals can update
if is_treatment_prof:
# Only add fields that were actually submitted
if post.get('body_location'):
vals['body_location'] = post.get('body_location')
if post.get('injury_type'):
vals['injury_type'] = post.get('injury_type')
if post.get('severity'):
vals['severity'] = post.get('severity')
if post.get('internal_notes'):
vals['internal_notes'] = post.get('internal_notes')

View file

@ -10,6 +10,7 @@ access_patient_contact_portal_tp,Portal TP Access for Patient Contacts,model_spo
access_injury_treatment_pro,Treatment Professional Access for Injuries,model_sports_patient_injury,group_sports_clinic_treatment_professional,1,1,1,1
access_injury_portal,Portal Access for Injuries,model_sports_patient_injury,base.group_portal,1,0,1,0
access_injury_portal_tp,Portal Treatment Prof Access for Injuries,model_sports_patient_injury,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_injury_portal_coach,Portal Coach Access for Injuries,model_sports_patient_injury,bemade_sports_clinic.group_portal_team_coach,1,1,1,0
access_team_user,User Access for Teams,model_sports_team,group_sports_clinic_user,1,1,1,0
access_team_admin,Admin Access for Teams,model_sports_team,group_sports_clinic_admin,1,1,1,1
access_team_treatment_pro,Treatment Professional Access for Teams,model_sports_team,group_sports_clinic_treatment_professional,1,1,1,0
@ -36,10 +37,13 @@ access_mail_alias_portal_tp,Portal TP Access for Aliases,mail.model_mail_alias,b
access_ir_model_portal_tp,Portal TP Access for Models,base.model_ir_model,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_mail_message_subtype_portal_tp,Portal TP Access for Message Subtypes,mail.model_mail_message_subtype,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_mail_template_portal_tp,Portal TP Access for Mail Templates,mail.model_mail_template,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_mail_template_portal_coach,Portal Coach Access for Mail Templates,mail.model_mail_template,bemade_sports_clinic.group_portal_team_coach,1,0,0,0
access_mail_notification_portal_tp,Portal TP Access for Mail Notifications,mail.model_mail_notification,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_ir_attachment_portal_tp,Portal TP Access for Attachments,base.model_ir_attachment,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_res_users_portal_tp,Portal TP Access for Users,base.model_res_users,bemade_sports_clinic.group_portal_treatment_professional,1,0,0,0
access_res_partner_portal_tp,Portal TP Access for Partners,base.model_res_partner,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_res_partner_portal_coach,Portal Coach Access for Partners,base.model_res_partner,bemade_sports_clinic.group_portal_team_coach,1,1,1,0
access_mail_followers_portal_tp,Portal TP Access for Followers,mail.model_mail_followers,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0
access_mail_followers_portal_coach,Portal Coach Access for Followers,mail.model_mail_followers,bemade_sports_clinic.group_portal_team_coach,1,1,1,0
access_bus_bus_portal_tp,Portal TP Access for Bus Messages,bus.model_bus_bus,bemade_sports_clinic.group_portal_treatment_professional,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
10 access_injury_treatment_pro Treatment Professional Access for Injuries model_sports_patient_injury group_sports_clinic_treatment_professional 1 1 1 1
11 access_injury_portal Portal Access for Injuries model_sports_patient_injury base.group_portal 1 0 1 0
12 access_injury_portal_tp Portal Treatment Prof Access for Injuries model_sports_patient_injury bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
13 access_injury_portal_coach Portal Coach Access for Injuries model_sports_patient_injury bemade_sports_clinic.group_portal_team_coach 1 1 1 0
14 access_team_user User Access for Teams model_sports_team group_sports_clinic_user 1 1 1 0
15 access_team_admin Admin Access for Teams model_sports_team group_sports_clinic_admin 1 1 1 1
16 access_team_treatment_pro Treatment Professional Access for Teams model_sports_team group_sports_clinic_treatment_professional 1 1 1 0
37 access_ir_model_portal_tp Portal TP Access for Models base.model_ir_model bemade_sports_clinic.group_portal_treatment_professional 1 0 0 0
38 access_mail_message_subtype_portal_tp Portal TP Access for Message Subtypes mail.model_mail_message_subtype bemade_sports_clinic.group_portal_treatment_professional 1 0 0 0
39 access_mail_template_portal_tp Portal TP Access for Mail Templates mail.model_mail_template bemade_sports_clinic.group_portal_treatment_professional 1 0 0 0
40 access_mail_template_portal_coach Portal Coach Access for Mail Templates mail.model_mail_template bemade_sports_clinic.group_portal_team_coach 1 0 0 0
41 access_mail_notification_portal_tp Portal TP Access for Mail Notifications mail.model_mail_notification bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
42 access_ir_attachment_portal_tp Portal TP Access for Attachments base.model_ir_attachment bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
43 access_res_users_portal_tp Portal TP Access for Users base.model_res_users bemade_sports_clinic.group_portal_treatment_professional 1 0 0 0
44 access_res_partner_portal_tp Portal TP Access for Partners base.model_res_partner bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
45 access_mail_followers_portal_tp access_res_partner_portal_coach Portal TP Access for Followers Portal Coach Access for Partners mail.model_mail_followers base.model_res_partner bemade_sports_clinic.group_portal_treatment_professional bemade_sports_clinic.group_portal_team_coach 1 1 1 0
46 access_mail_followers_portal_tp Portal TP Access for Followers mail.model_mail_followers bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
47 access_bus_bus_portal_tp access_mail_followers_portal_coach Portal TP Access for Bus Messages Portal Coach Access for Followers bus.model_bus_bus mail.model_mail_followers bemade_sports_clinic.group_portal_treatment_professional bemade_sports_clinic.group_portal_team_coach 1 1 1 0
48 access_bus_bus_portal_tp Portal TP Access for Bus Messages bus.model_bus_bus bemade_sports_clinic.group_portal_treatment_professional 1 1 1 0
49

View file

@ -84,5 +84,17 @@
<field name="perm_unlink" eval="False"/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
<!-- Portal team coaches can access partners linked to their team's patients -->
<record id="portal_team_coach_partner_access" model="ir.rule">
<field name="name">Portal Team Coach Access to Partners</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="groups" eval="[(6, 0, [ref('bemade_sports_clinic.group_portal_team_coach')])]"/>
<field name="perm_create" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_read" eval="True"/>
<field name="perm_unlink" eval="False"/>
<field name="domain_force">[(1, '=', 1)]</field>
</record>
</data>
</odoo>

View file

@ -36,61 +36,81 @@
<input type="hidden" name="injury_id" t-att-value="injury.id"/>
<input type="hidden" name="return_url" t-att-value="return_url"/>
<!-- Top Action Buttons -->
<div class="row mb-4">
<div class="col-lg-12">
<div class="d-flex justify-content-between">
<a t-att-href="return_url" class="btn btn-secondary">
<i class="fa fa-times"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
<!-- Injury Details Section -->
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Injury Details</h4>
</div>
<div class="card-body">
<!-- Basic injury information -->
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label for="injury_date">Date of Injury</label>
<div class="d-flex align-items-center">
<input type="date" name="injury_date" id="injury_date" class="form-control"
t-att-value="injury.injury_date" t-att-disabled="injury.injury_date_na"/>
<div class="form-check ml-3">
<input type="checkbox" name="injury_date_na" id="injury_date_na" class="form-check-input"
t-att-checked="injury.injury_date_na"/>
<label for="injury_date_na" class="form-check-label">N/A</label>
</div>
</div>
<small class="text-muted">Check N/A if exact date is unknown</small>
</div>
</div>
</div>
<!-- Diagnosis -->
<div class="row mt-3">
<div class="col-lg-12">
<div class="form-group">
<label for="diagnosis">Diagnosis/Description</label>
<textarea name="diagnosis" id="diagnosis" class="form-control"
rows="3" t-att-disabled="not is_treatment_prof and injury.stage != 'unverified'"><t t-esc="injury.diagnosis"/></textarea>
</div>
</div>
</div>
<!-- Treatment dates and status -->
<div t-if="is_treatment_prof" class="row mt-3">
<div class="col-lg-6">
<div class="form-group">
<label for="external_notes">External Notes</label>
<textarea name="external_notes" id="external_notes" class="form-control"
rows="3"><t t-esc="injury.external_notes"/></textarea>
<small class="text-muted">Notes visible to all team members</small>
<label for="predicted_resolution_date">Predicted Resolution Date</label>
<input type="date" name="predicted_resolution_date" id="predicted_resolution_date" class="form-control"
t-att-value="injury.predicted_resolution_date"/>
<small class="text-muted">Expected recovery timeline</small>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="resolution_date">Resolution Date</label>
<input type="date" name="resolution_date" id="resolution_date" class="form-control"
t-att-value="injury.resolution_date"/>
<small class="text-muted">Actual recovery date (when resolved)</small>
</div>
</div>
</div>
<!-- Fields only visible/editable to treatment professionals -->
<!-- Status and treatment professionals -->
<div t-if="is_treatment_prof" class="row mt-3">
<div class="col-lg-6">
<div class="form-group">
<label for="body_location">Body Location</label>
<input type="text" class="form-control" name="body_location" id="body_location" t-att-value="injury.body_location"/>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="injury_type">Injury Type</label>
<input type="text" class="form-control" name="injury_type" id="injury_type" t-att-value="injury.injury_type"/>
</div>
</div>
</div>
<!-- More treatment professional fields -->
<div t-if="is_treatment_prof" class="row mt-3">
<div class="col-lg-6">
<div class="form-group">
<label for="severity">Severity</label>
<select name="severity" id="severity" class="form-control">
<option value="">-- Select Severity --</option>
<t t-foreach="severity_options" t-as="option">
<option t-att-value="option[0]" t-att-selected="option[0] == injury.severity">
<t t-esc="option[1]"/>
</option>
</t>
</select>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="stage">Status</label>
@ -103,6 +123,19 @@
</select>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="treatment_professional_ids">Treatment Professionals</label>
<select name="treatment_professional_ids" id="treatment_professional_ids" class="form-control" multiple="multiple">
<t t-foreach="treatment_professionals" t-as="tp">
<option t-att-value="tp.id" t-att-selected="tp.id in injury.treatment_professional_ids.ids">
<t t-esc="tp.name"/>
</option>
</t>
</select>
<small class="text-muted">Hold Ctrl/Cmd to select multiple professionals</small>
</div>
</div>
</div>
<!-- Parental consent field (only visible to treatment professionals) -->
@ -153,6 +186,25 @@
</div>
</div>
<!-- External Notes Section -->
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
<h4 class="mb-0">External Notes</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-lg-12">
<div class="form-group">
<label for="external_notes">External Notes</label>
<textarea name="external_notes" id="external_notes" class="form-control"
rows="4"><t t-esc="injury.external_notes"/></textarea>
<small class="text-muted">Notes visible to all team members</small>
</div>
</div>
</div>
</div>
</div>
<!-- Action buttons -->
<div class="row mt-4">
<div class="col-lg-12">
@ -218,13 +270,8 @@
<h2 t-if="context == 'injury'">Injury Treatment Notes</h2>
<h2 t-else="">Patient Treatment Notes</h2>
<!-- Different back button based on context -->
<t t-if="context == 'injury'">
<a t-att-href="'/my/injury/edit?injury_id=%s' % injury.id" class="btn btn-secondary">
<i class="fa fa-arrow-left"></i> Back to Injury
</a>
</t>
<t t-else="">
<!-- Back button only for patient context -->
<t t-if="context == 'patient'">
<a t-att-href="'/my/player?player_id=%s' % patient.id" class="btn btn-secondary">
<i class="fa fa-arrow-left"></i> Back to Patient
</a>

View file

@ -15,6 +15,20 @@
<input type="hidden" name="patient_id" t-att-value="patient.id"/>
<input type="hidden" name="return_url" t-att-value="return_url"/>
<!-- Top Action Buttons -->
<div class="row mb-4">
<div class="col-lg-12">
<div class="d-flex justify-content-between">
<a t-att-href="return_url" class="btn btn-secondary">
<i class="fa fa-times"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">
@ -231,6 +245,20 @@
<input type="hidden" name="contact_id" t-att-value="contact.id"/>
<input type="hidden" name="return_url" t-att-value="return_url"/>
<!-- Top Action Buttons -->
<div class="row mb-4">
<div class="col-lg-12">
<div class="d-flex justify-content-between">
<a t-att-href="return_url" class="btn btn-secondary">
<i class="fa fa-times"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-12">
<div class="form-group">

View file

@ -316,44 +316,44 @@
<div class="card-body">
<div class="row align-items-center">
<div class="col-md-3">
<h6 class="mb-1">Match Status</h6>
<span t-if="player.match_status == 'yes'" class="badge badge-success badge-lg" style="color: #000 !important;">
<h6 class="mb-1">Match</h6>
<span t-if="player.match_status == 'yes'" class="text-success font-weight-bold h5">
<i class="fa fa-check-circle"></i> Yes
</span>
<span t-elif="player.match_status == 'no'" class="badge badge-danger badge-lg" style="color: #fff !important;">
<span t-elif="player.match_status == 'no'" class="text-danger font-weight-bold h5">
<i class="fa fa-times-circle"></i> No
</span>
<span t-else="" class="badge badge-secondary badge-lg" style="color: #fff !important;">
<span t-else="" class="text-muted font-weight-bold h5">
<i class="fa fa-question-circle"></i> <span t-esc="player.match_status or 'Unknown'"/>
</span>
</div>
<div class="col-md-3">
<h6 class="mb-1">Practice Status</h6>
<span t-if="player.practice_status == 'yes'" class="badge badge-success badge-lg" style="color: #000 !important;">
<h6 class="mb-1">Practice</h6>
<span t-if="player.practice_status == 'yes'" class="text-success font-weight-bold h5">
<i class="fa fa-check-circle"></i> Yes
</span>
<span t-elif="player.practice_status == 'no_contact'" class="badge badge-warning badge-lg" style="color: #000 !important;">
<i class="fa fa-exclamation-triangle"></i> Yes, no contact
<span t-elif="player.practice_status == 'no_contact'" class="text-warning font-weight-bold h5">
<i class="fa fa-exclamation-triangle"></i> Yes, No Contact
</span>
<span t-elif="player.practice_status == 'no'" class="badge badge-danger badge-lg" style="color: #fff !important;">
<span t-elif="player.practice_status == 'no'" class="text-danger font-weight-bold h5">
<i class="fa fa-times-circle"></i> No
</span>
<span t-else="" class="badge badge-secondary badge-lg" style="color: #fff !important;">
<span t-else="" class="text-muted font-weight-bold h5">
<i class="fa fa-question-circle"></i> <span t-esc="player.practice_status or 'Unknown'"/>
</span>
</div>
<div class="col-md-3" t-if="player.stage">
<h6 class="mb-1">Status</h6>
<span t-if="player.stage == 'healthy'" class="badge badge-success badge-lg" style="color: #000 !important;">
<span t-if="player.stage == 'healthy'" class="text-success font-weight-bold h5">
<i class="fa fa-heart"></i> Play OK
</span>
<span t-elif="player.stage == 'practice_ok'" class="badge badge-warning badge-lg" style="color: #000 !important;">
<span t-elif="player.stage == 'practice_ok'" class="text-warning font-weight-bold h5">
<i class="fa fa-running"></i> Practice OK
</span>
<span t-elif="player.stage == 'no_play'" class="badge badge-danger badge-lg" style="color: #fff !important;">
<span t-elif="player.stage == 'no_play'" class="text-danger font-weight-bold h5">
<i class="fa fa-ban"></i> Injured
</span>
<span t-else="" class="badge badge-secondary badge-lg" style="color: #fff !important;">
<span t-else="" class="text-muted font-weight-bold h5">
<i class="fa fa-question-circle"></i> <span t-esc="player.stage"/>
</span>
</div>
@ -413,55 +413,60 @@
<tr>
<th>Date</th>
<th>Injury</th>
<th>Status</th>
<th>External Notes</th>
<t t-if="is_treatment_prof">
<th>Internal Notes</th>
<t t-if="not is_treatment_prof">
<th>External Notes</th>
</t>
<t t-if="is_treatment_prof">
<th class="text-end">Actions</th>
</t>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<t t-foreach="injuries" t-as="injury">
<tr t-attf-class="{{ 'text-danger' if injury.stage == 'unverified' else 'text-warning' if injury.stage == 'active' else '' }}">
<tr t-attf-class="{{ 'text-warning' if injury.stage == 'unverified' else 'text-danger' if injury.stage == 'active' else '' }}">
<td>
<span t-field="injury.injury_date" class="text-wrap" t-options="{'widget': 'date'}"/>
<t t-if="is_treatment_prof">
<a t-att-href="'/my/injury/edit?injury_id=%s' % injury.id" style="text-decoration: none; color: inherit;">
<span t-field="injury.injury_date" class="text-wrap" t-options="{'widget': 'date'}"/>
</a>
</t>
<t t-else="">
<span t-field="injury.injury_date" class="text-wrap" t-options="{'widget': 'date'}"/>
</t>
</td>
<td>
<span t-field="injury.diagnosis" class="text-wrap"/>
<t t-if="is_treatment_prof">
<a t-att-href="'/my/injury/edit?injury_id=%s' % injury.id" style="text-decoration: none; color: inherit;">
<span t-field="injury.diagnosis" class="text-wrap"/>
</a>
</t>
<t t-else="">
<span t-field="injury.diagnosis" class="text-wrap"/>
</t>
</td>
<td>
<span t-if="injury.stage == 'unverified'">Unverified</span>
<span t-elif="injury.stage == 'active'">Active</span>
<span t-elif="injury.stage == 'resolved'">Resolved</span>
</td>
<td>
<div t-if="injury.external_notes" class="text-wrap">
<span t-out="injury.external_notes"/>
</div>
<div t-else="" class="text-muted">No external notes</div>
</td>
<t t-if="is_treatment_prof">
<t t-if="not is_treatment_prof">
<td>
<div t-if="injury.internal_notes" class="text-wrap">
<span t-out="injury.internal_notes"/>
<div t-if="injury.external_notes" class="text-wrap">
<span t-out="injury.external_notes"/>
</div>
<div t-else="" class="text-muted">No external notes</div>
</td>
</t>
<t t-if="is_treatment_prof">
<td class="text-end">
<div class="btn-group">
<a t-att-href="'/my/injury/edit?injury_id=%s' % injury.id" class="btn btn-sm btn-primary">
<i class="fa fa-edit"></i> Edit
</a>
<a t-att-href="'/my/injury/notes?injury_id=%s' % injury.id" class="btn btn-sm btn-info">
<i class="fa fa-clipboard-list"></i> Notes
</a>
<a t-att-href="'/my/injury/documents?injury_id=%s' % injury.id" class="btn btn-sm btn-secondary">
<i class="fa fa-file-medical"></i> Docs
</a>
</div>
<div t-else="" class="text-muted">No internal notes</div>
</td>
</t>
<td class="text-end">
<div class="btn-group">
<a t-if="is_treatment_prof" t-att-href="'/my/injury/edit?injury_id=%s' % injury.id" class="btn btn-sm btn-primary">
<i class="fa fa-edit"></i> Edit
</a>
<a t-if="is_treatment_prof" t-att-href="'/my/injury/notes?injury_id=%s' % injury.id" class="btn btn-sm btn-info">
<i class="fa fa-clipboard-list"></i> Notes
</a>
<a t-if="is_treatment_prof" t-att-href="'/my/injury/documents?injury_id=%s' % injury.id" class="btn btn-sm btn-secondary">
<i class="fa fa-file-medical"></i> Docs
</a>
</div>
</td>
</tr>
</t>
</tbody>
@ -498,28 +503,10 @@
<td><strong>Age:</strong></td>
<td t-esc="player.age"/>
</tr>
<tr t-if="is_treatment_prof and player.last_consultation_date">
<tr t-if="is_treatment_prof">
<td><strong>Last Consultation:</strong></td>
<td t-esc="player.last_consultation_date"/>
</tr>
<tr t-if="is_treatment_prof and player.injured_since">
<td><strong>Injured Since:</strong></td>
<td>
<span t-esc="player.injured_since"/>
<small class="text-muted ms-2">
(<span t-esc="(datetime.date.today() - player.injured_since).days if player.injured_since else 0"/> days)
</small>
</td>
</tr>
<tr t-if="is_treatment_prof and player.active_injury_count > 0">
<td><strong>Active Injuries:</strong></td>
<td>
<span class="badge badge-warning badge-lg">
<i class="fa fa-exclamation-triangle"></i>
<span t-esc="player.active_injury_count"/> active
</span>
</td>
</tr>
<tr t-if="is_treatment_prof and player.allergies">
<td><strong>Allergies:</strong></td>
<td>
@ -611,21 +598,21 @@
<h5>Status Information</h5>
<table class="table table-borderless">
<tr>
<td><strong>Match Status:</strong></td>
<td>
<span t-if="player.match_status == 'yes'" class="badge badge-success">Yes</span>
<span t-elif="player.match_status == 'no'" class="badge badge-danger">No</span>
<span t-else="" class="badge badge-secondary" t-esc="player.match_status"/>
</td>
<td><strong>Match:</strong></td>
<td>
<span t-if="player.match_status == 'yes'" class="text-success font-weight-bold"><i class="fa fa-check-circle"></i> Yes</span>
<span t-elif="player.match_status == 'no'" class="text-danger font-weight-bold"><i class="fa fa-times-circle"></i> No</span>
<span t-else="" class="text-muted" t-esc="player.match_status"/>
</td>
</tr>
<tr>
<td><strong>Practice Status:</strong></td>
<td>
<span t-if="player.practice_status == 'yes'" class="badge badge-success">Available</span>
<span t-elif="player.practice_status == 'no_contact'" class="badge badge-warning">Available, No Contact</span>
<span t-elif="player.practice_status == 'no'" class="badge badge-danger">Not Available</span>
<span t-else="" class="badge badge-secondary" t-esc="player.practice_status"/>
</td>
<td><strong>Practice:</strong></td>
<td>
<span t-if="player.practice_status == 'yes'" class="text-success font-weight-bold"><i class="fa fa-check-circle"></i> Yes</span>
<span t-elif="player.practice_status == 'no_contact'" class="text-warning font-weight-bold"><i class="fa fa-exclamation-triangle"></i> Yes, No Contact</span>
<span t-elif="player.practice_status == 'no'" class="text-danger font-weight-bold"><i class="fa fa-times-circle"></i> No</span>
<span t-else="" class="text-muted" t-esc="player.practice_status"/>
</td>
</tr>
<tr t-if="player.predicted_return_date">
<td><strong>Predicted Return Date:</strong></td>
@ -636,13 +623,13 @@
<td t-esc="player.return_date"/>
</tr>
<tr t-if="player.stage">
<td><strong>Current Stage:</strong></td>
<td>
<span t-if="player.stage == 'healthy'" class="badge badge-success">Healthy</span>
<span t-elif="player.stage == 'practice_ok'" class="badge badge-warning">Practice OK</span>
<span t-elif="player.stage == 'no_play'" class="badge badge-danger">No Play</span>
<span t-else="" class="badge badge-secondary" t-esc="player.stage"/>
</td>
<td><strong>Status:</strong></td>
<td>
<span t-if="player.stage == 'healthy'" class="text-success font-weight-bold"><i class="fa fa-heart"></i> Play OK</span>
<span t-elif="player.stage == 'practice_ok'" class="text-warning font-weight-bold"><i class="fa fa-running"></i> Practice OK</span>
<span t-elif="player.stage == 'no_play'" class="text-danger font-weight-bold"><i class="fa fa-ban"></i> Injured</span>
<span t-else="" class="text-muted" t-esc="player.stage"/>
</td>
</tr>
</table>

View file

@ -11,19 +11,7 @@
<form action="/my/patient/injury/create" method="post" class="mt-3">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input type="hidden" name="patient_id" t-att-value="patient.id"/>
<div class="form-group row">
<label for="team_id" class="col-md-3 col-form-label">Team</label>
<div class="col-md-9">
<select class="form-control" id="team_id" name="team_id" required="required">
<t t-if="not default_team_id">
<option value="">-- Select Team --</option>
</t>
<t t-foreach="teams" t-as="team">
<option t-att-value="team.id" t-att-selected="team.id == default_team_id"><t t-esc="team.name"/></option>
</t>
</select>
</div>
</div>
<div class="form-group row">
<label for="injury_date" class="col-md-3 col-form-label">Injury Date</label>
<div class="col-md-9">

View file

@ -30,7 +30,7 @@
<group>
<group col="2">
<field name="patient_id" invisible="1"/>
<field name="team_id"/>
<field name="team_id" groups="base.group_no_one"/>
<span colspan="2">
<label for="injury_date"/>
<field name="injury_date" widget="date" class="oe_inline"/>
@ -62,7 +62,7 @@
multi_edit="True">
<field name="patient_id" column_invisible="True"/>
<field name="injury_date" widget="date"/>
<field name="team_id"/>
<field name="team_id" groups="base.group_no_one"/>
<field name="diagnosis"/>
<field name="predicted_resolution_date" widget="date"/>
<field name="resolution_date" widget="date"/>

View file

@ -341,6 +341,20 @@
<input type="hidden" name="activity_id" t-att-value="activity.id"/>
<input type="hidden" name="return_url" value="/my/activities"/>
<!-- Top Action Buttons -->
<div class="row mb-4">
<div class="col-lg-12">
<div class="d-flex justify-content-between">
<a href="/my/activities" class="btn btn-secondary">
<i class="fa fa-times"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i> Save Changes
</button>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="form-group">