update api doc
This commit is contained in:
parent
fe0a643607
commit
41bb9b5dd7
1
.gitignore
vendored
1
.gitignore
vendored
@ -104,3 +104,4 @@ dump.rdb
|
||||
|
||||
node_modules
|
||||
data/Documents
|
||||
deploy/target.dev-home.yaml
|
||||
39
.playwright-cli/page-2026-07-12T20-33-12-565Z.yml
Normal file
39
.playwright-cli/page-2026-07-12T20-33-12-565Z.yml
Normal file
@ -0,0 +1,39 @@
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "HH Logo" [ref=e6]
|
||||
- heading "Welcome to PX360" [level=1] [ref=e7]
|
||||
- paragraph [ref=e8]: Patient Experience Management System
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]:
|
||||
- generic [ref=e12]:
|
||||
- img [ref=e13]
|
||||
- text: Email Address
|
||||
- generic [ref=e16]:
|
||||
- img [ref=e17]
|
||||
- textbox "Email Address" [active] [ref=e20]:
|
||||
- /placeholder: Enter your email
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- img [ref=e23]
|
||||
- text: Password
|
||||
- generic [ref=e26]:
|
||||
- img [ref=e27]
|
||||
- textbox "Password" [ref=e31]:
|
||||
- /placeholder: Enter your password
|
||||
- button [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e33]
|
||||
- generic [ref=e36]:
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- checkbox "Remember me" [ref=e38]
|
||||
- generic [ref=e39]: Remember me
|
||||
- link "Forgot password?" [ref=e40] [cursor=pointer]:
|
||||
- /url: /accounts/password/reset/
|
||||
- button "Sign In" [ref=e41] [cursor=pointer]:
|
||||
- img [ref=e42]
|
||||
- text: Sign In
|
||||
- generic [ref=e45]:
|
||||
- paragraph [ref=e46]:
|
||||
- text: Secure login powered by
|
||||
- link "tenhal.sa" [ref=e47] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
- paragraph [ref=e48]: © 2026 Al Hammadi Hospital
|
||||
41
.playwright-cli/page-2026-07-12T20-33-40-832Z.yml
Normal file
41
.playwright-cli/page-2026-07-12T20-33-40-832Z.yml
Normal file
@ -0,0 +1,41 @@
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "HH Logo" [ref=e6]
|
||||
- heading "Welcome to PX360" [level=1] [ref=e7]
|
||||
- paragraph [ref=e8]: Patient Experience Management System
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]:
|
||||
- generic [ref=e12]:
|
||||
- img [ref=e49]
|
||||
- text: Email Address
|
||||
- generic [ref=e16]:
|
||||
- img [ref=e52]
|
||||
- textbox "Email Address" [ref=e20]:
|
||||
- /placeholder: Enter your email
|
||||
- text: e2e-px-employee@px360.test
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- img [ref=e55]
|
||||
- text: Password
|
||||
- generic [ref=e26]:
|
||||
- img [ref=e58]
|
||||
- textbox "Password" [ref=e31]:
|
||||
- /placeholder: Enter your password
|
||||
- text: Dev@123456
|
||||
- button [active] [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e62]
|
||||
- generic [ref=e36]:
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- checkbox "Remember me" [ref=e38]
|
||||
- generic [ref=e39]: Remember me
|
||||
- link "Forgot password?" [ref=e40] [cursor=pointer]:
|
||||
- /url: /accounts/password/reset/
|
||||
- button "Sign In" [ref=e41] [cursor=pointer]:
|
||||
- img [ref=e67]
|
||||
- text: Sign In
|
||||
- generic [ref=e45]:
|
||||
- paragraph [ref=e46]:
|
||||
- text: Secure login powered by
|
||||
- link "tenhal.sa" [ref=e47] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
- paragraph [ref=e48]: © 2026 Al Hammadi Hospital
|
||||
227
.playwright-cli/page-2026-07-12T20-33-55-371Z.yml
Normal file
227
.playwright-cli/page-2026-07-12T20-33-55-371Z.yml
Normal file
@ -0,0 +1,227 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Departments" [ref=e141] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: Contact Center Department
|
||||
- generic [ref=e147]:
|
||||
- generic [ref=e148]:
|
||||
- img [ref=e150]
|
||||
- generic [ref=e154]:
|
||||
- heading "Contact Center Department" [level=1] [ref=e155]
|
||||
- paragraph [ref=e156]: قسم مركز الاتصال
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: hh_n_contact_center_department
|
||||
- generic [ref=e162]: Administrative
|
||||
- generic [ref=e163]:
|
||||
- img [ref=e164]
|
||||
- text: E2E Test Hospital
|
||||
- generic [ref=e168]:
|
||||
- generic [ref=e170]:
|
||||
- img [ref=e172]
|
||||
- generic [ref=e177]:
|
||||
- paragraph [ref=e178]: Staff
|
||||
- paragraph [ref=e179]: "4"
|
||||
- link "Open Complaints 3 3 total" [ref=e180] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/complaints/?status=open
|
||||
- generic [ref=e181]:
|
||||
- img [ref=e183]
|
||||
- generic [ref=e186]:
|
||||
- paragraph [ref=e187]: Open Complaints
|
||||
- paragraph [ref=e188]: "3"
|
||||
- paragraph [ref=e189]: 3 total
|
||||
- link "Pending Inquiries 0 2 total" [ref=e190] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/inquiries/?status=open
|
||||
- generic [ref=e191]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e196]:
|
||||
- paragraph [ref=e197]: Pending Inquiries
|
||||
- paragraph [ref=e198]: "0"
|
||||
- paragraph [ref=e199]: 2 total
|
||||
- link "Open Observations 1 1 total" [ref=e200] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/observations/?status=open
|
||||
- generic [ref=e201]:
|
||||
- img [ref=e203]
|
||||
- generic [ref=e206]:
|
||||
- paragraph [ref=e207]: Open Observations
|
||||
- paragraph [ref=e208]: "1"
|
||||
- paragraph [ref=e209]: 1 total
|
||||
- generic [ref=e210]:
|
||||
- generic [ref=e211]:
|
||||
- img [ref=e213]
|
||||
- generic [ref=e215]:
|
||||
- heading "Pending Actions" [level=3] [ref=e216]
|
||||
- paragraph [ref=e217]: 3 items awaiting response
|
||||
- table [ref=e219]:
|
||||
- rowgroup [ref=e220]:
|
||||
- row "Type Reference Subject SLA Deadline Action" [ref=e221]:
|
||||
- columnheader "Type" [ref=e222]
|
||||
- columnheader "Reference" [ref=e223]
|
||||
- columnheader "Subject" [ref=e224]
|
||||
- columnheader "SLA Deadline" [ref=e225]
|
||||
- columnheader "Action" [ref=e226]
|
||||
- rowgroup [ref=e227]:
|
||||
- row "Complaint Response CMP-202607-0006 Test Complaint - Analysis Required - Respond" [ref=e228]:
|
||||
- cell "Complaint Response" [ref=e229]:
|
||||
- generic [ref=e230]: Complaint Response
|
||||
- cell "CMP-202607-0006" [ref=e231]
|
||||
- cell "Test Complaint - Analysis Required" [ref=e232]
|
||||
- cell "-" [ref=e233]
|
||||
- cell "Respond" [ref=e234]:
|
||||
- link "Respond" [ref=e236] [cursor=pointer]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/explain/dM0ilaUpzA1OuAceyjvwV9oxYA0geW_jsgipvrYUt_g/
|
||||
- img [ref=e237]
|
||||
- text: Respond
|
||||
- row "Complaint Explanation CMP-202607-0005 Test Complaint - No Details - Respond" [ref=e239]:
|
||||
- cell "Complaint Explanation" [ref=e240]:
|
||||
- generic [ref=e241]: Complaint Explanation
|
||||
- cell "CMP-202607-0005" [ref=e242]
|
||||
- cell "Test Complaint - No Details" [ref=e243]
|
||||
- cell "-" [ref=e244]
|
||||
- cell "Respond" [ref=e245]:
|
||||
- link "Respond" [ref=e247] [cursor=pointer]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/explain/iBIf0PrMq1RZYSEIDxSn4WZik4PFk6dunoNwarFTnXI/
|
||||
- img [ref=e248]
|
||||
- text: Respond
|
||||
- row "Complaint Explanation CMP-202607-0005 Test Complaint - No Details - Respond" [ref=e252]:
|
||||
- cell "Complaint Explanation" [ref=e253]:
|
||||
- generic [ref=e254]: Complaint Explanation
|
||||
- cell "CMP-202607-0005" [ref=e255]
|
||||
- cell "Test Complaint - No Details" [ref=e256]
|
||||
- cell "-" [ref=e257]
|
||||
- cell "Respond" [ref=e258]:
|
||||
- link "Respond" [ref=e260] [cursor=pointer]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/explain/X1Zd54Tb1NcExZeRr6ACN5RtPCwB_RTKeqNeFcVP24E/
|
||||
- img [ref=e261]
|
||||
- text: Respond
|
||||
- generic [ref=e266]:
|
||||
- button "Dashboard" [ref=e267] [cursor=pointer]:
|
||||
- img [ref=e268]
|
||||
- text: Dashboard
|
||||
- button "Staff (4)" [ref=e270] [cursor=pointer]:
|
||||
- img [ref=e271]
|
||||
- text: Staff (4)
|
||||
- button "Roles" [ref=e276] [cursor=pointer]:
|
||||
- img [ref=e277]
|
||||
- text: Roles
|
||||
- button "Sections (2)" [ref=e289] [cursor=pointer]:
|
||||
- img [ref=e290]
|
||||
- text: Sections (2)
|
||||
- button "Complaints (3)" [ref=e294] [cursor=pointer]:
|
||||
- img [ref=e295]
|
||||
- text: Complaints (3)
|
||||
- button "Inquiries (2)" [ref=e298] [cursor=pointer]:
|
||||
- img [ref=e299]
|
||||
- text: Inquiries (2)
|
||||
- button "Observations (1)" [ref=e302] [cursor=pointer]:
|
||||
- img [ref=e303]
|
||||
- text: Observations (1)
|
||||
- button "Suggestions (1)" [ref=e306] [cursor=pointer]:
|
||||
- img [ref=e307]
|
||||
- text: Suggestions (1)
|
||||
- button "Appreciations (2)" [ref=e309] [cursor=pointer]:
|
||||
- img [ref=e310]
|
||||
- text: Appreciations (2)
|
||||
- button "Standards (0)" [ref=e312] [cursor=pointer]:
|
||||
- img [ref=e313]
|
||||
- text: Standards (0)
|
||||
- button "Projects (8)" [ref=e316] [cursor=pointer]:
|
||||
- img [ref=e317]
|
||||
- text: Projects (8)
|
||||
- paragraph [ref=e324]: Loading analytics...
|
||||
- contentinfo [ref=e325]:
|
||||
- paragraph [ref=e326]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e327] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
258
.playwright-cli/page-2026-07-12T20-34-06-540Z.yml
Normal file
258
.playwright-cli/page-2026-07-12T20-34-06-540Z.yml
Normal file
@ -0,0 +1,258 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Inquiries Console" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage patient inquiries and requests
|
||||
- link "New Inquiry" [ref=e144] [cursor=pointer]:
|
||||
- /url: /inquiries/new/
|
||||
- img [ref=e145]
|
||||
- text: New Inquiry
|
||||
- generic [ref=e146]:
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e150]
|
||||
- generic [ref=e153]:
|
||||
- heading "Total Inquiries" [level=6] [ref=e154]
|
||||
- heading "5" [level=2] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e159]
|
||||
- generic [ref=e162]:
|
||||
- heading "Resolved" [level=6] [ref=e163]
|
||||
- heading "2 (40.0%)" [level=2] [ref=e164]
|
||||
- generic [ref=e165]:
|
||||
- img [ref=e168]
|
||||
- generic [ref=e171]:
|
||||
- heading "In Progress" [level=6] [ref=e172]
|
||||
- heading "1" [level=2] [ref=e173]
|
||||
- generic [ref=e174]:
|
||||
- img [ref=e177]
|
||||
- generic [ref=e179]:
|
||||
- heading "Overdue" [level=6] [ref=e180]
|
||||
- heading "0" [level=2] [ref=e181]
|
||||
- generic [ref=e182]:
|
||||
- generic [ref=e183]:
|
||||
- img [ref=e185]
|
||||
- heading "Filters" [level=5] [ref=e186]
|
||||
- generic [ref=e188]:
|
||||
- generic [ref=e189]:
|
||||
- generic [ref=e190]: Search
|
||||
- textbox "Subject, contact name..." [ref=e191]
|
||||
- generic [ref=e192]:
|
||||
- generic [ref=e193]: Status
|
||||
- combobox [ref=e194]:
|
||||
- option "All Status" [selected]
|
||||
- option "Open"
|
||||
- option "In Progress"
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e195]:
|
||||
- generic [ref=e196]: Category
|
||||
- combobox [ref=e197]:
|
||||
- option "All Categories" [selected]
|
||||
- option "General"
|
||||
- option "Appointments"
|
||||
- option "Billing"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- button "Filter" [ref=e198] [cursor=pointer]:
|
||||
- img [ref=e199]
|
||||
- text: Filter
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]:
|
||||
- generic [ref=e204]:
|
||||
- img [ref=e206]
|
||||
- heading "All Inquiries" [level=5] [ref=e209]
|
||||
- generic [ref=e210]: "Total: 5"
|
||||
- paragraph [ref=e211]: "Showing: 1-5 of 5"
|
||||
- generic [ref=e212]:
|
||||
- table [ref=e213]:
|
||||
- rowgroup [ref=e214]:
|
||||
- row "Reference Subject Contact Category Status Priority Created Assigned" [ref=e215]:
|
||||
- columnheader "Reference" [ref=e216]
|
||||
- columnheader "Subject" [ref=e217]
|
||||
- columnheader "Contact" [ref=e218]
|
||||
- columnheader "Category" [ref=e219]
|
||||
- columnheader "Status" [ref=e220]
|
||||
- columnheader "Priority" [ref=e221]
|
||||
- columnheader "Created" [ref=e222]
|
||||
- columnheader "Assigned" [ref=e223]
|
||||
- rowgroup [ref=e224]:
|
||||
- row "INQ-202607-0007 test12121212 ismail mosa 0569874563 General Information Open Medium Jul 12, 2026 Unassigned" [ref=e225] [cursor=pointer]:
|
||||
- cell "INQ-202607-0007" [ref=e226]:
|
||||
- code [ref=e227]: INQ-202607-0007
|
||||
- cell "test12121212" [ref=e228]:
|
||||
- link "test12121212" [ref=e229]:
|
||||
- /url: /inquiries/b4a65498-e8d8-403c-82bb-ae09adc7fc1c/
|
||||
- cell "ismail mosa 0569874563" [ref=e230]:
|
||||
- generic [ref=e231]: ismail mosa
|
||||
- generic [ref=e232]: "0569874563"
|
||||
- cell "General Information" [ref=e233]
|
||||
- cell "Open" [ref=e234]
|
||||
- cell "Medium" [ref=e235]
|
||||
- cell "Jul 12, 2026" [ref=e236]
|
||||
- cell "Unassigned" [ref=e237]
|
||||
- row "INQ-202607-0006 test12121212 test 0569874563 General Information Open Medium Jul 12, 2026 Unassigned" [ref=e238] [cursor=pointer]:
|
||||
- cell "INQ-202607-0006" [ref=e239]:
|
||||
- code [ref=e240]: INQ-202607-0006
|
||||
- cell "test12121212" [ref=e241]:
|
||||
- link "test12121212" [ref=e242]:
|
||||
- /url: /inquiries/1c3eda08-ff03-4521-bcb8-ec137c79b417/
|
||||
- cell "test 0569874563" [ref=e243]:
|
||||
- generic [ref=e244]: test
|
||||
- generic [ref=e245]: "0569874563"
|
||||
- cell "General Information" [ref=e246]
|
||||
- cell "Open" [ref=e247]
|
||||
- cell "Medium" [ref=e248]
|
||||
- cell "Jul 12, 2026" [ref=e249]
|
||||
- cell "Unassigned" [ref=e250]
|
||||
- row "INQ-202607-0005 test test 0569874589 General Information In Progress Medium Jul 09, 2026 Unassigned" [ref=e251] [cursor=pointer]:
|
||||
- cell "INQ-202607-0005" [ref=e252]:
|
||||
- code [ref=e253]: INQ-202607-0005
|
||||
- cell "test" [ref=e254]:
|
||||
- link "test" [ref=e255]:
|
||||
- /url: /inquiries/c05309c2-3518-46ae-9b1c-c144acdb5d58/
|
||||
- cell "test 0569874589" [ref=e256]:
|
||||
- generic [ref=e257]: test
|
||||
- generic [ref=e258]: "0569874589"
|
||||
- cell "General Information" [ref=e259]
|
||||
- cell "In Progress" [ref=e260]
|
||||
- cell "Medium" [ref=e261]
|
||||
- cell "Jul 09, 2026" [ref=e262]
|
||||
- cell "Unassigned" [ref=e263]
|
||||
- row "INQ-202607-0004 hi ismail 0569874589 General Information Resolved Medium Jul 09, 2026 ismail mosa" [ref=e264] [cursor=pointer]:
|
||||
- cell "INQ-202607-0004" [ref=e265]:
|
||||
- code [ref=e266]: INQ-202607-0004
|
||||
- cell "hi" [ref=e267]:
|
||||
- link "hi" [ref=e268]:
|
||||
- /url: /inquiries/d1df0b83-5c16-4f51-b735-e89f612deb50/
|
||||
- cell "ismail 0569874589" [ref=e269]:
|
||||
- generic [ref=e270]: ismail
|
||||
- generic [ref=e271]: "0569874589"
|
||||
- cell "General Information" [ref=e272]
|
||||
- cell "Resolved" [ref=e273]
|
||||
- cell "Medium" [ref=e274]
|
||||
- cell "Jul 09, 2026" [ref=e275]
|
||||
- cell "ismail mosa" [ref=e276]
|
||||
- row "INQ-202607-0003 test test 0569874589 General Information Resolved Medium Jul 08, 2026 ismail mosa" [ref=e277] [cursor=pointer]:
|
||||
- cell "INQ-202607-0003" [ref=e278]:
|
||||
- code [ref=e279]: INQ-202607-0003
|
||||
- cell "test" [ref=e280]:
|
||||
- link "test" [ref=e281]:
|
||||
- /url: /inquiries/031cbd06-e2dc-4cd9-9b81-b1218e090d6c/
|
||||
- cell "test 0569874589" [ref=e282]:
|
||||
- generic [ref=e283]: test
|
||||
- generic [ref=e284]: "0569874589"
|
||||
- cell "General Information" [ref=e285]
|
||||
- cell "Resolved" [ref=e286]
|
||||
- cell "Medium" [ref=e287]
|
||||
- cell "Jul 08, 2026" [ref=e288]
|
||||
- cell "ismail mosa" [ref=e289]
|
||||
- generic [ref=e290]:
|
||||
- generic [ref=e291]:
|
||||
- generic [ref=e292]: Showing 1-5 of 5
|
||||
- generic [ref=e293]:
|
||||
- generic [ref=e294]: Show
|
||||
- combobox [ref=e295]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e296]:
|
||||
- img [ref=e298]
|
||||
- generic [ref=e300]: "1"
|
||||
- img [ref=e302]
|
||||
- contentinfo [ref=e304]:
|
||||
- paragraph [ref=e305]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e306] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
172
.playwright-cli/page-2026-07-12T20-34-28-605Z.yml
Normal file
172
.playwright-cli/page-2026-07-12T20-34-28-605Z.yml
Normal file
@ -0,0 +1,172 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Inquiries" [ref=e141] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: INQ-202607-0007
|
||||
- generic [ref=e145]: Open
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Not Sent to Dept
|
||||
- generic [ref=e150]:
|
||||
- heading "test12121212" [level=1] [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- img [ref=e154]
|
||||
- text: 2026-07-12
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]:
|
||||
- img [ref=e160]
|
||||
- generic [ref=e162]: Created
|
||||
- generic [ref=e164]:
|
||||
- img [ref=e166]
|
||||
- generic [ref=e169]: Assigned
|
||||
- generic [ref=e171]:
|
||||
- img [ref=e173]
|
||||
- generic [ref=e176]: Sent to Dept
|
||||
- generic [ref=e178]:
|
||||
- img [ref=e180]
|
||||
- generic [ref=e182]: Dept Response
|
||||
- generic [ref=e184]:
|
||||
- img [ref=e186]
|
||||
- generic [ref=e189]: Resolved
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e195]:
|
||||
- paragraph [ref=e196]: Activate this inquiry
|
||||
- paragraph [ref=e197]: Activation moves it to In Progress so actions become available.
|
||||
- generic [ref=e198]:
|
||||
- button "Cancel" [ref=e200] [cursor=pointer]
|
||||
- button "Activate" [ref=e202] [cursor=pointer]
|
||||
- navigation [ref=e203]:
|
||||
- button "Details" [ref=e204] [cursor=pointer]
|
||||
- button "Timeline" [ref=e205] [cursor=pointer]
|
||||
- button "Notes" [ref=e206] [cursor=pointer]
|
||||
- main [ref=e207]:
|
||||
- generic [ref=e209]:
|
||||
- generic [ref=e210]:
|
||||
- generic [ref=e211]:
|
||||
- paragraph
|
||||
- paragraph [ref=e212]: "231313131"
|
||||
- paragraph
|
||||
- generic [ref=e213]:
|
||||
- generic [ref=e215]:
|
||||
- img [ref=e216]
|
||||
- generic [ref=e219]: AI Analysis
|
||||
- generic [ref=e220]:
|
||||
- paragraph [ref=e221]: "231313131"
|
||||
- paragraph [ref=e222]: "231313131"
|
||||
- generic [ref=e223]:
|
||||
- heading "Patient" [level=3] [ref=e224]:
|
||||
- img [ref=e225]
|
||||
- text: Patient
|
||||
- generic [ref=e228]:
|
||||
- generic [ref=e229]: ismail mosa
|
||||
- generic [ref=e230]: "|"
|
||||
- generic [ref=e231]:
|
||||
- img [ref=e232]
|
||||
- text: "0569874563"
|
||||
- generic [ref=e235]:
|
||||
- heading "Quick Actions" [level=3] [ref=e236]
|
||||
- generic [ref=e237]:
|
||||
- button "Activate" [ref=e239] [cursor=pointer]:
|
||||
- img [ref=e240]
|
||||
- generic [ref=e243]: Activate
|
||||
- button "Cancel Inquiry" [ref=e245] [cursor=pointer]:
|
||||
- img [ref=e246]
|
||||
- generic [ref=e250]: Cancel Inquiry
|
||||
- contentinfo [ref=e251]:
|
||||
- paragraph [ref=e252]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e253] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
344
.playwright-cli/page-2026-07-12T20-34-49-571Z.yml
Normal file
344
.playwright-cli/page-2026-07-12T20-34-49-571Z.yml
Normal file
@ -0,0 +1,344 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Observations" [level=1] [ref=e142]:
|
||||
- img [ref=e143]
|
||||
- text: Observations
|
||||
- paragraph [ref=e146]: Manage and triage staff-reported observations
|
||||
- generic [ref=e147]:
|
||||
- link "New Observation" [ref=e148] [cursor=pointer]:
|
||||
- /url: /observations/create/
|
||||
- img [ref=e149]
|
||||
- text: New Observation
|
||||
- link "Public Form" [ref=e151] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e152]
|
||||
- text: Public Form
|
||||
- generic [ref=e153]:
|
||||
- generic [ref=e155]:
|
||||
- generic [ref=e156]:
|
||||
- paragraph [ref=e157]: Total
|
||||
- paragraph [ref=e158]: "2"
|
||||
- img [ref=e160]
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- paragraph [ref=e164]: New
|
||||
- paragraph
|
||||
- img [ref=e166]
|
||||
- generic [ref=e170]:
|
||||
- generic [ref=e171]:
|
||||
- paragraph [ref=e172]: In Progress
|
||||
- paragraph [ref=e173]: "2"
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- generic [ref=e179]:
|
||||
- img [ref=e181]
|
||||
- heading "Filters" [level=5] [ref=e183]
|
||||
- button [ref=e184] [cursor=pointer]:
|
||||
- img [ref=e185]
|
||||
- generic [ref=e188]:
|
||||
- generic [ref=e189]:
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Search
|
||||
- textbox "Tracking code, description..." [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- generic [ref=e194]: Status
|
||||
- combobox [ref=e195]:
|
||||
- option "All Statuses" [selected]
|
||||
- option "Open"
|
||||
- option "In Progress"
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e196]:
|
||||
- generic [ref=e197]: Severity
|
||||
- combobox [ref=e198]:
|
||||
- option "All Severities" [selected]
|
||||
- option "Low"
|
||||
- option "Medium"
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Category
|
||||
- combobox [ref=e201]:
|
||||
- option "All Categories" [selected]
|
||||
- option "Patient Safety"
|
||||
- option "Clinical Quality"
|
||||
- option "Infection Control"
|
||||
- option "Medication Safety"
|
||||
- option "Equipment & Devices"
|
||||
- option "Facility & Environment"
|
||||
- option "Staff Behavior"
|
||||
- option "Communication"
|
||||
- option "Documentation"
|
||||
- option "Process & Workflow"
|
||||
- option "Security"
|
||||
- option "IT & Systems"
|
||||
- option "Housekeeping"
|
||||
- option "Food Services"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Department
|
||||
- combobox [ref=e204]:
|
||||
- option "All Departments" [selected]
|
||||
- option "Contact Center Department"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Executive Administration"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Security Department"
|
||||
- option "Anesthesia Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Surgery Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Nursing Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Laundry Department"
|
||||
- generic [ref=e206] [cursor=pointer]:
|
||||
- generic [ref=e207]: All Departments
|
||||
- combobox "All Departments" [ref=e208]
|
||||
- generic [ref=e209]:
|
||||
- generic [ref=e210]: Assigned To
|
||||
- combobox [ref=e211]:
|
||||
- option "All Users" [selected]
|
||||
- option "E2E PX Employee"
|
||||
- option "test test"
|
||||
- option "test test"
|
||||
- generic [ref=e212]:
|
||||
- generic [ref=e213]: Date From
|
||||
- textbox [ref=e214]
|
||||
- generic [ref=e215]:
|
||||
- generic [ref=e216]: Date To
|
||||
- textbox [ref=e217]
|
||||
- generic [ref=e218]:
|
||||
- button "Apply Filters" [ref=e219] [cursor=pointer]:
|
||||
- img [ref=e220]
|
||||
- text: Apply Filters
|
||||
- link "Clear" [ref=e223] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e224]
|
||||
- text: Clear
|
||||
- generic [ref=e229]:
|
||||
- img [ref=e231]
|
||||
- heading "Observations List" [level=5] [ref=e233]
|
||||
- generic [ref=e234]: Showing 1 to 4 of 4 observations
|
||||
- table [ref=e237]:
|
||||
- rowgroup [ref=e238]:
|
||||
- row "Reference Date Category Description Severity Status Reporter Department Assigned Actions" [ref=e239]:
|
||||
- columnheader "Reference" [ref=e240]
|
||||
- columnheader "Date" [ref=e241]
|
||||
- columnheader "Category" [ref=e242]
|
||||
- columnheader "Description" [ref=e243]
|
||||
- columnheader "Severity" [ref=e244]
|
||||
- columnheader "Status" [ref=e245]
|
||||
- columnheader "Reporter" [ref=e246]
|
||||
- columnheader "Department" [ref=e247]
|
||||
- columnheader "Assigned" [ref=e248]
|
||||
- columnheader "Actions" [ref=e249]
|
||||
- rowgroup [ref=e250]:
|
||||
- row "OBS-202607-0001 Jul 08, 2026 16:33 - test observation Medium In Progress ismail mosa Contact Center Department ismail mosa" [ref=e251]:
|
||||
- cell "OBS-202607-0001" [ref=e252]
|
||||
- cell "Jul 08, 2026 16:33" [ref=e253]:
|
||||
- text: Jul 08, 2026
|
||||
- text: 16:33
|
||||
- cell "-" [ref=e254]
|
||||
- cell "test observation" [ref=e255]:
|
||||
- generic [ref=e256]: test observation
|
||||
- cell "Medium" [ref=e257]
|
||||
- cell "In Progress" [ref=e258]
|
||||
- cell "ismail mosa" [ref=e259]
|
||||
- cell "Contact Center Department" [ref=e260]
|
||||
- cell "ismail mosa" [ref=e261]
|
||||
- cell [ref=e262]:
|
||||
- link [ref=e263] [cursor=pointer]:
|
||||
- /url: /observations/1d925706-110e-4bad-808f-26aae2029144/
|
||||
- img [ref=e264]
|
||||
- row "OBS-202606-E2EHOSP-0056 Jun 22, 2026 10:16 Other Observation i notice some trash in the hall Medium In Progress E2E PX Admin Contact Center Department E2E PX Admin" [ref=e267]:
|
||||
- cell "OBS-202606-E2EHOSP-0056" [ref=e268]
|
||||
- cell "Jun 22, 2026 10:16" [ref=e269]:
|
||||
- text: Jun 22, 2026
|
||||
- text: 10:16
|
||||
- cell "Other" [ref=e270]:
|
||||
- generic [ref=e271]: Other
|
||||
- cell "Observation i notice some trash in the hall" [ref=e272]:
|
||||
- generic [ref=e273]:
|
||||
- strong [ref=e274]: Observation
|
||||
- text: i notice some trash in the hall
|
||||
- cell "Medium" [ref=e275]
|
||||
- cell "In Progress" [ref=e276]
|
||||
- cell "E2E PX Admin" [ref=e277]
|
||||
- cell "Contact Center Department" [ref=e278]
|
||||
- cell "E2E PX Admin" [ref=e279]
|
||||
- cell [ref=e280]:
|
||||
- link [ref=e281] [cursor=pointer]:
|
||||
- /url: /observations/b9a80c99-31dc-4cda-a1e3-c20d94a28729/
|
||||
- img [ref=e282]
|
||||
- row "OBS-O8TCIC Jun 09, 2026 13:36 - Wet floor near the main entrance, no warning sign posted. Medium In Progress Anonymous Reporter - ismail mosa" [ref=e285]:
|
||||
- cell "OBS-O8TCIC" [ref=e286]
|
||||
- cell "Jun 09, 2026 13:36" [ref=e287]:
|
||||
- text: Jun 09, 2026
|
||||
- text: 13:36
|
||||
- cell "-" [ref=e288]
|
||||
- cell "Wet floor near the main entrance, no warning sign posted." [ref=e289]:
|
||||
- generic [ref=e290]: Wet floor near the main entrance, no warning sign posted.
|
||||
- cell "Medium" [ref=e291]
|
||||
- cell "In Progress" [ref=e292]
|
||||
- cell "Anonymous Reporter" [ref=e293]
|
||||
- cell "-" [ref=e294]
|
||||
- cell "ismail mosa" [ref=e295]
|
||||
- cell [ref=e296]:
|
||||
- link [ref=e297] [cursor=pointer]:
|
||||
- /url: /observations/5f8fc6c6-3b38-4b88-8144-3f926c24e290/
|
||||
- img [ref=e298]
|
||||
- row "OBS-QPE02F Jun 07, 2026 21:08 Other Observation safsafsad Medium Open ismail Daif - Unassigned" [ref=e301]:
|
||||
- cell "OBS-QPE02F" [ref=e302]
|
||||
- cell "Jun 07, 2026 21:08" [ref=e303]:
|
||||
- text: Jun 07, 2026
|
||||
- text: 21:08
|
||||
- cell "Other" [ref=e304]:
|
||||
- generic [ref=e305]: Other
|
||||
- cell "Observation safsafsad" [ref=e306]:
|
||||
- generic [ref=e307]:
|
||||
- strong [ref=e308]: Observation
|
||||
- text: safsafsad
|
||||
- cell "Medium" [ref=e309]
|
||||
- cell "Open" [ref=e310]
|
||||
- cell "ismail Daif" [ref=e311]
|
||||
- cell "-" [ref=e312]
|
||||
- cell "Unassigned" [ref=e313]
|
||||
- cell [ref=e314]:
|
||||
- link [ref=e315] [cursor=pointer]:
|
||||
- /url: /observations/327d9b79-2126-4800-8cd7-647f006271ce/
|
||||
- img [ref=e316]
|
||||
- generic [ref=e319]:
|
||||
- generic [ref=e320]:
|
||||
- generic [ref=e321]: Showing 1-4 of 4
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e323]: Show
|
||||
- combobox [ref=e324]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e325]:
|
||||
- img [ref=e327]
|
||||
- generic [ref=e329]: "1"
|
||||
- img [ref=e331]
|
||||
- contentinfo [ref=e333]:
|
||||
- paragraph [ref=e334]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e335] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
259
.playwright-cli/page-2026-07-12T20-35-19-622Z.yml
Normal file
259
.playwright-cli/page-2026-07-12T20-35-19-622Z.yml
Normal file
@ -0,0 +1,259 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Observations" [ref=e141] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: OBS-202607-0001
|
||||
- generic [ref=e145]: In Progress
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Not Sent to Dept
|
||||
- heading "test observation" [level=1] [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- generic [ref=e154]:
|
||||
- img [ref=e156]
|
||||
- generic [ref=e158]: Created
|
||||
- generic [ref=e160]:
|
||||
- img [ref=e162]
|
||||
- generic [ref=e164]: Assigned
|
||||
- generic [ref=e166]:
|
||||
- img [ref=e168]
|
||||
- generic [ref=e171]: Sent to Dept
|
||||
- generic [ref=e173]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e177]: Dept Response
|
||||
- generic [ref=e179]:
|
||||
- img [ref=e181]
|
||||
- generic [ref=e184]: Resolved
|
||||
- navigation [ref=e185]:
|
||||
- button "Details" [ref=e186] [cursor=pointer]
|
||||
- button "Timeline" [ref=e187] [cursor=pointer]
|
||||
- button "RCA" [ref=e188] [cursor=pointer]
|
||||
- button "Notes" [ref=e189] [cursor=pointer]
|
||||
- main [ref=e190]:
|
||||
- generic [ref=e192]:
|
||||
- generic [ref=e193]:
|
||||
- paragraph [ref=e195]: test observation
|
||||
- generic [ref=e196]:
|
||||
- generic [ref=e197]:
|
||||
- paragraph [ref=e198]: Category
|
||||
- paragraph [ref=e199]: "-"
|
||||
- generic [ref=e200]:
|
||||
- paragraph [ref=e201]: Location
|
||||
- paragraph [ref=e202]: "-"
|
||||
- generic [ref=e203]:
|
||||
- paragraph [ref=e204]: Incident Date
|
||||
- paragraph [ref=e205]: 08 Jul 2026
|
||||
- generic [ref=e209]:
|
||||
- img [ref=e210]
|
||||
- paragraph [ref=e214]: No department assigned to this observation
|
||||
- generic [ref=e215]:
|
||||
- generic [ref=e216]:
|
||||
- heading "Quick Actions" [level=3] [ref=e217]
|
||||
- generic [ref=e218]:
|
||||
- button "Respond to Patient" [ref=e219] [cursor=pointer]:
|
||||
- img [ref=e220]
|
||||
- generic [ref=e222]: Respond to Patient
|
||||
- button "Reassign" [ref=e223] [cursor=pointer]:
|
||||
- img [ref=e224]
|
||||
- generic [ref=e227]: Reassign
|
||||
- button "Send to Dept" [ref=e228] [cursor=pointer]:
|
||||
- img [ref=e229]
|
||||
- generic [ref=e232]: Send to Dept
|
||||
- button "Resolve" [ref=e234] [cursor=pointer]:
|
||||
- img [ref=e235]
|
||||
- generic [ref=e238]: Resolve
|
||||
- link "RCA" [ref=e239] [cursor=pointer]:
|
||||
- /url: /rca/create/?related_model=observation&related_id=1d925706-110e-4bad-808f-26aae2029144
|
||||
- img [ref=e240]
|
||||
- generic [ref=e243]: RCA
|
||||
- link "QI Project" [ref=e244] [cursor=pointer]:
|
||||
- /url: /projects/create/?related_model=observation&related_id=1d925706-110e-4bad-808f-26aae2029144
|
||||
- img [ref=e245]
|
||||
- generic [ref=e247]: QI Project
|
||||
- button "Delete" [ref=e249] [cursor=pointer]:
|
||||
- img [ref=e250]
|
||||
- generic [ref=e253]: Delete
|
||||
- generic [ref=e254]:
|
||||
- heading "Assignment" [level=3] [ref=e255]:
|
||||
- img [ref=e256]
|
||||
- text: Assignment
|
||||
- list [ref=e261]:
|
||||
- listitem [ref=e262]:
|
||||
- generic [ref=e263]: Department
|
||||
- generic [ref=e264]: Contact Center Department
|
||||
- listitem [ref=e265]:
|
||||
- generic [ref=e266]: Assigned To
|
||||
- generic [ref=e267]: ismail mosa
|
||||
- generic [ref=e268]:
|
||||
- heading "Linked PX Action" [level=3] [ref=e269]:
|
||||
- img [ref=e270]
|
||||
- text: Linked PX Action
|
||||
- paragraph [ref=e273]: Observation OBS-202607-0001
|
||||
- link "View Action" [ref=e274] [cursor=pointer]:
|
||||
- /url: /actions/9b185c2a-e3fe-4e13-a727-7aab21df8754/
|
||||
- img [ref=e275]
|
||||
- text: View Action
|
||||
- generic [ref=e277]:
|
||||
- heading "Triage" [level=3] [ref=e278]:
|
||||
- img [ref=e279]
|
||||
- text: Triage
|
||||
- generic [ref=e280]:
|
||||
- generic [ref=e281]:
|
||||
- generic [ref=e282]: Department
|
||||
- combobox [ref=e283]:
|
||||
- option "Select department"
|
||||
- option "Contact Center Department" [selected]
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Executive Administration"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Security Department"
|
||||
- option "Anesthesia Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Surgery Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Nursing Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Laundry Department"
|
||||
- generic [ref=e284]:
|
||||
- generic [ref=e285]: Assign To
|
||||
- combobox [ref=e286]:
|
||||
- option "Select assignee" [selected]
|
||||
- option "E2E PX Employee (e2e-px-employee@px360.test)"
|
||||
- option "test test (9m5s6gzmft@ruutukf.com)"
|
||||
- option "test test (esma3el555@gmail.com)"
|
||||
- generic [ref=e287]:
|
||||
- generic [ref=e288]: Status
|
||||
- combobox [ref=e289]:
|
||||
- option "Open"
|
||||
- option "In Progress" [selected]
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e290]:
|
||||
- generic [ref=e291]: Note
|
||||
- textbox "Add a note about this triage action (optional)..." [ref=e292]
|
||||
- button "Update" [ref=e293] [cursor=pointer]:
|
||||
- img [ref=e294]
|
||||
- text: Update
|
||||
- contentinfo [ref=e297]:
|
||||
- paragraph [ref=e298]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e299] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
201
.playwright-cli/page-2026-07-12T20-35-37-343Z.yml
Normal file
201
.playwright-cli/page-2026-07-12T20-35-37-343Z.yml
Normal file
@ -0,0 +1,201 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Suggestions" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage and review submitted suggestions
|
||||
- link "New Suggestion" [ref=e145] [cursor=pointer]:
|
||||
- /url: /suggestions/create/
|
||||
- img [ref=e146]
|
||||
- text: New Suggestion
|
||||
- generic [ref=e147]:
|
||||
- generic [ref=e148]:
|
||||
- img [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- heading "Total Suggestions" [level=6] [ref=e154]
|
||||
- heading "3" [level=2] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e159]
|
||||
- generic [ref=e161]:
|
||||
- heading "Compliments" [level=6] [ref=e162]
|
||||
- heading "0" [level=2] [ref=e163]
|
||||
- generic [ref=e164]:
|
||||
- img [ref=e167]
|
||||
- generic [ref=e169]:
|
||||
- heading "Avg Rating" [level=6] [ref=e170]
|
||||
- heading "0.0 /5" [level=2] [ref=e171]
|
||||
- generic [ref=e172]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- heading "Pending Review" [level=6] [ref=e179]
|
||||
- heading "2" [level=2] [ref=e180]
|
||||
- generic [ref=e181]:
|
||||
- generic [ref=e182]:
|
||||
- generic [ref=e183]:
|
||||
- img [ref=e185]
|
||||
- heading "Suggestions" [level=5] [ref=e187]
|
||||
- generic [ref=e188]:
|
||||
- link "All" [ref=e189] [cursor=pointer]:
|
||||
- /url: "?"
|
||||
- link "Suggestions" [ref=e190] [cursor=pointer]:
|
||||
- /url: "?feedback_type=suggestion"
|
||||
- link "Compliments" [ref=e191] [cursor=pointer]:
|
||||
- /url: "?feedback_type=compliment"
|
||||
- button "Advanced" [ref=e193] [cursor=pointer]:
|
||||
- img [ref=e194]
|
||||
- text: Advanced
|
||||
- paragraph [ref=e195]: "Showing: 1-3 of 3"
|
||||
- table [ref=e197]:
|
||||
- rowgroup [ref=e198]:
|
||||
- row "ID Type Contact Title Category Rating Sentiment Status Created Actions" [ref=e199]:
|
||||
- columnheader "ID" [ref=e200]
|
||||
- columnheader "Type" [ref=e201]
|
||||
- columnheader "Contact" [ref=e202]
|
||||
- columnheader "Title" [ref=e203]
|
||||
- columnheader "Category" [ref=e204]
|
||||
- columnheader "Rating" [ref=e205]
|
||||
- columnheader "Sentiment" [ref=e206]
|
||||
- columnheader "Status" [ref=e207]
|
||||
- columnheader "Created" [ref=e208]
|
||||
- columnheader "Actions" [ref=e209]
|
||||
- rowgroup [ref=e210]:
|
||||
- row "SGT-202607-0003 Suggestion ismail Daif t Other - Neutral Submitted Jul 09, 2026" [ref=e211] [cursor=pointer]:
|
||||
- cell "SGT-202607-0003" [ref=e212]
|
||||
- cell "Suggestion" [ref=e213]
|
||||
- cell "ismail Daif" [ref=e214]:
|
||||
- generic [ref=e215]: ismail Daif
|
||||
- cell "t" [ref=e216]:
|
||||
- generic [ref=e217]: t
|
||||
- cell "Other" [ref=e218]
|
||||
- cell "-" [ref=e219]
|
||||
- cell "Neutral" [ref=e220]
|
||||
- cell "Submitted" [ref=e221]
|
||||
- cell "Jul 09, 2026" [ref=e222]
|
||||
- cell [ref=e223]:
|
||||
- link "View" [ref=e225]:
|
||||
- /url: /suggestions/78f015c5-fbed-4e10-987f-aa8ced75545a/
|
||||
- img [ref=e226]
|
||||
- row "SGT-202607-0002 Suggestion ismail test Other - Neutral Submitted Jul 09, 2026" [ref=e229] [cursor=pointer]:
|
||||
- cell "SGT-202607-0002" [ref=e230]
|
||||
- cell "Suggestion" [ref=e231]
|
||||
- cell "ismail" [ref=e232]:
|
||||
- generic [ref=e233]: ismail
|
||||
- cell "test" [ref=e234]:
|
||||
- generic [ref=e235]: test
|
||||
- cell "Other" [ref=e236]
|
||||
- cell "-" [ref=e237]
|
||||
- cell "Neutral" [ref=e238]
|
||||
- cell "Submitted" [ref=e239]
|
||||
- cell "Jul 09, 2026" [ref=e240]
|
||||
- cell [ref=e241]:
|
||||
- link "View" [ref=e243]:
|
||||
- /url: /suggestions/04b009f3-53aa-4eac-9baf-39b3633e56b0/
|
||||
- img [ref=e244]
|
||||
- row "SGT-202607-0001 Suggestion ismail The suggestion titled 'test' with content 'test' appears … Other - Neutral Closed Jul 09, 2026" [ref=e247] [cursor=pointer]:
|
||||
- cell "SGT-202607-0001" [ref=e248]
|
||||
- cell "Suggestion" [ref=e249]
|
||||
- cell "ismail" [ref=e250]:
|
||||
- generic [ref=e251]: ismail
|
||||
- cell "The suggestion titled 'test' with content 'test' appears …" [ref=e252]:
|
||||
- generic [ref=e253]: The suggestion titled 'test' with content 'test' appears …
|
||||
- cell "Other" [ref=e254]
|
||||
- cell "-" [ref=e255]
|
||||
- cell "Neutral" [ref=e256]
|
||||
- cell "Closed" [ref=e257]
|
||||
- cell "Jul 09, 2026" [ref=e258]
|
||||
- cell [ref=e259]:
|
||||
- link "View" [ref=e261]:
|
||||
- /url: /suggestions/ef1e633b-7fae-46e2-baab-0d5bdd71e7b7/
|
||||
- img [ref=e262]
|
||||
- contentinfo [ref=e265]:
|
||||
- paragraph [ref=e266]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e267] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
188
.playwright-cli/page-2026-07-12T20-35-50-710Z.yml
Normal file
188
.playwright-cli/page-2026-07-12T20-35-50-710Z.yml
Normal file
@ -0,0 +1,188 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Suggestions" [ref=e141] [cursor=pointer]:
|
||||
- /url: /suggestions/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: test
|
||||
- generic [ref=e145]: Submitted
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- heading "Suggestion Detail" [level=1] [ref=e148]
|
||||
- link "Edit" [ref=e150] [cursor=pointer]:
|
||||
- /url: /suggestions/04b009f3-53aa-4eac-9baf-39b3633e56b0/update/
|
||||
- img [ref=e151]
|
||||
- text: Edit
|
||||
- generic [ref=e155]:
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e158]
|
||||
- generic [ref=e160]: Submitted
|
||||
- generic [ref=e162]:
|
||||
- img [ref=e164]
|
||||
- generic [ref=e167]: Assigned
|
||||
- generic [ref=e169]:
|
||||
- img [ref=e171]
|
||||
- generic [ref=e174]: Reviewed
|
||||
- generic [ref=e176]:
|
||||
- img [ref=e178]
|
||||
- generic [ref=e180]: Acknowledged
|
||||
- generic [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e187]: Closed
|
||||
- navigation [ref=e188]:
|
||||
- button "Details" [ref=e189] [cursor=pointer]
|
||||
- button "AI Analysis" [ref=e190] [cursor=pointer]
|
||||
- button "Notes" [ref=e191] [cursor=pointer]
|
||||
- main [ref=e192]:
|
||||
- generic [ref=e195]:
|
||||
- heading "Suggestion Details" [level=3] [ref=e196]:
|
||||
- img [ref=e197]
|
||||
- text: Suggestion Details
|
||||
- paragraph [ref=e200]: test
|
||||
- generic [ref=e201]:
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Hospital
|
||||
- generic [ref=e204]: E2E Test Hospital
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Category
|
||||
- generic [ref=e207]: Other
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Location
|
||||
- generic [ref=e210]: Not specified
|
||||
- generic [ref=e211]:
|
||||
- generic [ref=e212]: Sentiment
|
||||
- generic [ref=e214]: Neutral
|
||||
- generic [ref=e215]:
|
||||
- heading "Contact" [level=6] [ref=e216]
|
||||
- generic [ref=e217]:
|
||||
- generic [ref=e218]: ismail
|
||||
- generic [ref=e219]: "0569874589"
|
||||
- generic [ref=e221]:
|
||||
- img [ref=e222]
|
||||
- text: July 09, 2026 13:46
|
||||
- generic [ref=e225]:
|
||||
- heading "Quick Actions" [level=3] [ref=e226]
|
||||
- generic [ref=e227]:
|
||||
- generic [ref=e228]:
|
||||
- text: Status
|
||||
- combobox [ref=e229]:
|
||||
- option "Submitted" [selected]
|
||||
- option "Reviewed"
|
||||
- option "Acknowledged"
|
||||
- option "Closed"
|
||||
- option "Reopened"
|
||||
- textbox "Add a note..." [ref=e230]
|
||||
- button "Update" [ref=e231] [cursor=pointer]:
|
||||
- img [ref=e232]
|
||||
- text: Update
|
||||
- separator [ref=e237]
|
||||
- generic [ref=e238]:
|
||||
- text: Assign To
|
||||
- combobox [ref=e239]:
|
||||
- option "Select user..." [selected]
|
||||
- option "E2E PX Employee"
|
||||
- option "test test"
|
||||
- option "test test"
|
||||
- button "Assign" [ref=e240] [cursor=pointer]:
|
||||
- img [ref=e241]
|
||||
- text: Assign
|
||||
- separator [ref=e245]
|
||||
- generic [ref=e246]:
|
||||
- text: Department
|
||||
- button "Send to Department" [ref=e247] [cursor=pointer]:
|
||||
- img [ref=e248]
|
||||
- text: Send to Department
|
||||
- separator [ref=e251]
|
||||
- contentinfo [ref=e252]:
|
||||
- paragraph [ref=e253]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e254] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
221
.playwright-cli/page-2026-07-12T20-36-00-595Z.yml
Normal file
221
.playwright-cli/page-2026-07-12T20-36-00-595Z.yml
Normal file
@ -0,0 +1,221 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Appreciations" [level=1] [ref=e142]:
|
||||
- img [ref=e143]
|
||||
- text: Appreciations
|
||||
- paragraph [ref=e145]: Manage and review patient appreciation submissions
|
||||
- generic [ref=e146]:
|
||||
- link "Create Internal" [ref=e147] [cursor=pointer]:
|
||||
- /url: /appreciation/new/
|
||||
- img [ref=e148]
|
||||
- text: Create Internal
|
||||
- link "Public Form" [ref=e149] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e150]
|
||||
- text: Public Form
|
||||
- generic [ref=e154]:
|
||||
- paragraph [ref=e155]: "0"
|
||||
- paragraph [ref=e156]: Pending
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e159]:
|
||||
- img [ref=e161]
|
||||
- generic [ref=e164]:
|
||||
- paragraph [ref=e165]: Total
|
||||
- paragraph [ref=e166]: "3"
|
||||
- generic [ref=e168]:
|
||||
- img [ref=e170]
|
||||
- generic [ref=e173]:
|
||||
- paragraph [ref=e174]: Draft
|
||||
- paragraph [ref=e175]: "0"
|
||||
- generic [ref=e177]:
|
||||
- img [ref=e179]
|
||||
- generic [ref=e181]:
|
||||
- paragraph [ref=e182]: Activated
|
||||
- paragraph [ref=e183]: "0"
|
||||
- generic [ref=e185]:
|
||||
- img [ref=e187]
|
||||
- generic [ref=e190]:
|
||||
- paragraph [ref=e191]: Sent
|
||||
- paragraph [ref=e192]: "3"
|
||||
- generic [ref=e193]:
|
||||
- link "Leaderboard See top performers" [ref=e194] [cursor=pointer]:
|
||||
- /url: /appreciation/leaderboard/
|
||||
- generic [ref=e196]:
|
||||
- img [ref=e198]
|
||||
- generic [ref=e204]:
|
||||
- heading "Leaderboard" [level=3] [ref=e205]
|
||||
- paragraph [ref=e206]: See top performers
|
||||
- link "My Badges View earned badges" [ref=e207] [cursor=pointer]:
|
||||
- /url: /appreciation/badges/
|
||||
- generic [ref=e209]:
|
||||
- img [ref=e211]
|
||||
- generic [ref=e214]:
|
||||
- heading "My Badges" [level=3] [ref=e215]
|
||||
- paragraph [ref=e216]: View earned badges
|
||||
- generic [ref=e217]:
|
||||
- generic [ref=e218]:
|
||||
- img [ref=e220]
|
||||
- heading "Filters" [level=3] [ref=e222]
|
||||
- generic [ref=e224]:
|
||||
- generic [ref=e225]:
|
||||
- generic [ref=e226]: Search
|
||||
- textbox "Search messages..." [ref=e227]
|
||||
- generic [ref=e228]:
|
||||
- generic [ref=e229]: Status
|
||||
- combobox [ref=e230]:
|
||||
- option "All Status" [selected]
|
||||
- option "Draft"
|
||||
- option "Activated"
|
||||
- option "AI Analyzed"
|
||||
- option "Sent"
|
||||
- option "Acknowledged"
|
||||
- button "Search" [ref=e231] [cursor=pointer]:
|
||||
- img [ref=e232]
|
||||
- text: Search
|
||||
- link "Clear" [ref=e235] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- table [ref=e237]:
|
||||
- rowgroup [ref=e238]:
|
||||
- row "From Message Staff Department Category Status Date Actions" [ref=e239]:
|
||||
- columnheader "From" [ref=e240]
|
||||
- columnheader "Message" [ref=e241]
|
||||
- columnheader "Staff" [ref=e242]
|
||||
- columnheader "Department" [ref=e243]
|
||||
- columnheader "Category" [ref=e244]
|
||||
- columnheader "Status" [ref=e245]
|
||||
- columnheader "Date" [ref=e246]
|
||||
- columnheader "Actions" [ref=e247]
|
||||
- rowgroup [ref=e248]:
|
||||
- row "ismail thank - Contact Center Department - Sent Jul 09, 2026" [ref=e249] [cursor=pointer]:
|
||||
- cell "ismail" [ref=e250]
|
||||
- cell "thank" [ref=e251]:
|
||||
- generic [ref=e252]: thank
|
||||
- cell "-" [ref=e253]
|
||||
- cell "Contact Center Department" [ref=e254]
|
||||
- cell "-" [ref=e255]
|
||||
- cell "Sent" [ref=e256]
|
||||
- cell "Jul 09, 2026" [ref=e257]
|
||||
- cell [ref=e258]:
|
||||
- link "View" [ref=e260]:
|
||||
- /url: /appreciation/detail/5306821e-d596-498f-88c5-fa3addcdd491/
|
||||
- img [ref=e261]
|
||||
- row "ismail mosa test - Contact Center Department - Sent Jul 09, 2026" [ref=e264] [cursor=pointer]:
|
||||
- cell "ismail mosa" [ref=e265]
|
||||
- cell "test" [ref=e266]:
|
||||
- generic [ref=e267]: test
|
||||
- cell "-" [ref=e268]
|
||||
- cell "Contact Center Department" [ref=e269]
|
||||
- cell "-" [ref=e270]
|
||||
- cell "Sent" [ref=e271]
|
||||
- cell "Jul 09, 2026" [ref=e272]
|
||||
- cell [ref=e273]:
|
||||
- link "View" [ref=e275]:
|
||||
- /url: /appreciation/detail/3697b78b-5ff3-4392-8982-af20f58daebf/
|
||||
- img [ref=e276]
|
||||
- row "ismail mosa thank you - Contact Center Department - Sent Jul 09, 2026" [ref=e279] [cursor=pointer]:
|
||||
- cell "ismail mosa" [ref=e280]
|
||||
- cell "thank you" [ref=e281]:
|
||||
- generic [ref=e282]: thank you
|
||||
- cell "-" [ref=e283]
|
||||
- cell "Contact Center Department" [ref=e284]
|
||||
- cell "-" [ref=e285]
|
||||
- cell "Sent" [ref=e286]
|
||||
- cell "Jul 09, 2026" [ref=e287]
|
||||
- cell [ref=e288]:
|
||||
- link "View" [ref=e290]:
|
||||
- /url: /appreciation/detail/240ea5bd-a12d-4f76-8a79-a8dd9de5714a/
|
||||
- img [ref=e291]
|
||||
- contentinfo [ref=e294]:
|
||||
- paragraph [ref=e295]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e296] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
137
.playwright-cli/page-2026-07-12T20-36-13-189Z.yml
Normal file
137
.playwright-cli/page-2026-07-12T20-36-13-189Z.yml
Normal file
@ -0,0 +1,137 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Appreciations" [ref=e141] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: Detail
|
||||
- generic [ref=e145]: Sent
|
||||
- generic [ref=e146]:
|
||||
- heading "Appreciation Detail" [level=1] [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Appreciation Detail
|
||||
- generic [ref=e151]: 2026-07-09 13:55 — E2E Test Hospital
|
||||
- navigation [ref=e152]:
|
||||
- button "Details" [ref=e153] [cursor=pointer]
|
||||
- button "AI Analysis" [ref=e154] [cursor=pointer]
|
||||
- button "Notes" [ref=e155] [cursor=pointer]
|
||||
- main [ref=e156]:
|
||||
- generic [ref=e158]:
|
||||
- generic [ref=e159]:
|
||||
- heading "Appreciation Message" [level=3] [ref=e160]:
|
||||
- img [ref=e161]
|
||||
- text: Appreciation Message
|
||||
- paragraph
|
||||
- paragraph [ref=e163]: thank
|
||||
- paragraph
|
||||
- generic [ref=e164]:
|
||||
- heading "Submitted By" [level=3] [ref=e165]:
|
||||
- img [ref=e166]
|
||||
- text: Submitted By
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]:
|
||||
- paragraph [ref=e171]: Name
|
||||
- paragraph [ref=e172]: ismail
|
||||
- generic [ref=e173]:
|
||||
- paragraph [ref=e174]: Phone
|
||||
- paragraph [ref=e175]: "0569874589"
|
||||
- generic [ref=e176]:
|
||||
- heading "Sent" [level=3] [ref=e177]:
|
||||
- img [ref=e178]
|
||||
- text: Sent
|
||||
- generic [ref=e182]:
|
||||
- paragraph [ref=e183]: Sent At
|
||||
- paragraph [ref=e184]: 2026-07-09 13:57
|
||||
- contentinfo [ref=e186]:
|
||||
- paragraph [ref=e187]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e188] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
256
.playwright-cli/page-2026-07-12T20-36-24-641Z.yml
Normal file
256
.playwright-cli/page-2026-07-12T20-36-24-641Z.yml
Normal file
@ -0,0 +1,256 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- link "Inquiries" [ref=e143] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e144]
|
||||
- generic [ref=e146]: New Inquiry
|
||||
- heading "Create New Inquiry" [level=1] [ref=e147]
|
||||
- generic [ref=e149]:
|
||||
- generic [ref=e150]:
|
||||
- img [ref=e152]
|
||||
- heading "Inquiry Information" [level=5] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- generic [ref=e157]:
|
||||
- heading "Organization" [level=6] [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: Organization
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- generic [ref=e164]: Location Type *
|
||||
- combobox "Location Type *" [ref=e165]:
|
||||
- option "Select Location Type" [selected]
|
||||
- option "Outpatient"
|
||||
- option "Inpatient"
|
||||
- option "Emergency"
|
||||
- option "General"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]: Area
|
||||
- combobox "Area" [ref=e168]:
|
||||
- option "Select Area (optional)" [selected]
|
||||
- option "Anesthesia (E2E-HOSP)"
|
||||
- option "Dialysis (E2E-HOSP)"
|
||||
- option "Emergency (E2E-HOSP)"
|
||||
- option "Endoscopy (E2E-HOSP)"
|
||||
- option "ICCU (E2E-HOSP)"
|
||||
- option "ICU (E2E-HOSP)"
|
||||
- option "ICU Stepdown (E2E-HOSP)"
|
||||
- option "LTACU (E2E-HOSP)"
|
||||
- option "Labor & Delivery (E2E-HOSP)"
|
||||
- option "Main OR (E2E-HOSP)"
|
||||
- option "Medical Ward (E2E-HOSP)"
|
||||
- option "NICU - Neonatal Intensive Care Unit (E2E-HOSP)"
|
||||
- option "Nursery (E2E-HOSP)"
|
||||
- option "OB Wards (E2E-HOSP)"
|
||||
- option "OPD1 (E2E-HOSP)"
|
||||
- option "OPD2 (E2E-HOSP)"
|
||||
- option "OPD3 (E2E-HOSP)"
|
||||
- option "OPD4 (E2E-HOSP)"
|
||||
- option "OPD5 (E2E-HOSP)"
|
||||
- option "OPD6 (E2E-HOSP)"
|
||||
- option "OPD7 (E2E-HOSP)"
|
||||
- option "PICU (E2E-HOSP)"
|
||||
- option "PICU Stepdown (E2E-HOSP)"
|
||||
- option "Pediatric Ward (E2E-HOSP)"
|
||||
- option "Recovery (E2E-HOSP)"
|
||||
- option "Stepdown 3-4 (E2E-HOSP)"
|
||||
- option "Surgical Wards (E2E-HOSP)"
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]: Department Category *
|
||||
- combobox [ref=e171]:
|
||||
- option "Select Category" [selected]
|
||||
- option "Medical"
|
||||
- option "Nursing"
|
||||
- option "Administrative"
|
||||
- option "Support Services"
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Department (Optional)
|
||||
- combobox "Department (Optional)" [ref=e174]:
|
||||
- option "Select Department" [selected]
|
||||
- option "Anesthesia Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Contact Center Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "Executive Administration"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Laundry Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Nursing Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Security Department"
|
||||
- option "Surgery Department"
|
||||
- generic [ref=e175]:
|
||||
- generic [ref=e176]: Section
|
||||
- combobox "Section" [ref=e177]:
|
||||
- option "Select Section" [selected]
|
||||
- generic [ref=e178]:
|
||||
- heading "Contact Information" [level=6] [ref=e179]:
|
||||
- img [ref=e180]
|
||||
- text: Contact Information
|
||||
- generic [ref=e183]:
|
||||
- generic [ref=e184]:
|
||||
- generic [ref=e185]: Contact Name *
|
||||
- textbox "Contact Name *" [ref=e186]
|
||||
- generic [ref=e187]:
|
||||
- generic [ref=e188]: Contact Phone *
|
||||
- textbox "Contact Phone *" [ref=e189]
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Contact Email
|
||||
- textbox "Contact Email" [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- heading "Inquiry Details" [level=6] [ref=e194]:
|
||||
- img [ref=e195]
|
||||
- text: Inquiry Details
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Inquiry Type *
|
||||
- combobox "Inquiry Type *" [ref=e201]:
|
||||
- option "General Inquiry" [selected]
|
||||
- option "Appointment Related"
|
||||
- option "Billing & Insurance"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Priority
|
||||
- combobox "Priority" [ref=e204]:
|
||||
- option "Low"
|
||||
- option "Medium" [selected]
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Subject *
|
||||
- textbox "Subject *" [ref=e207]:
|
||||
- /placeholder: Brief subject
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Message *
|
||||
- textbox "Message *" [ref=e210]:
|
||||
- /placeholder: Describe your inquiry
|
||||
- generic [ref=e211]:
|
||||
- button "Create Inquiry" [ref=e212] [cursor=pointer]:
|
||||
- img [ref=e213]
|
||||
- text: Create Inquiry
|
||||
- link "Cancel" [ref=e216] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- contentinfo [ref=e217]:
|
||||
- paragraph [ref=e218]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e219] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
258
.playwright-cli/page-2026-07-12T20-37-03-048Z.yml
Normal file
258
.playwright-cli/page-2026-07-12T20-37-03-048Z.yml
Normal file
@ -0,0 +1,258 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- link "Inquiries" [ref=e143] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e144]
|
||||
- generic [ref=e146]: New Inquiry
|
||||
- heading "Create New Inquiry" [level=1] [ref=e147]
|
||||
- generic [ref=e149]:
|
||||
- generic [ref=e150]:
|
||||
- img [ref=e152]
|
||||
- heading "Inquiry Information" [level=5] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- generic [ref=e157]:
|
||||
- heading "Organization" [level=6] [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: Organization
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- generic [ref=e164]: Location Type *
|
||||
- combobox "Location Type *" [active] [ref=e165]:
|
||||
- option "Select Location Type" [selected]
|
||||
- option "Outpatient"
|
||||
- option "Inpatient"
|
||||
- option "Emergency"
|
||||
- option "General"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]: Area
|
||||
- combobox "Area" [ref=e168]:
|
||||
- option "Select Area (optional)" [selected]
|
||||
- option "Anesthesia (E2E-HOSP)"
|
||||
- option "Dialysis (E2E-HOSP)"
|
||||
- option "Emergency (E2E-HOSP)"
|
||||
- option "Endoscopy (E2E-HOSP)"
|
||||
- option "ICCU (E2E-HOSP)"
|
||||
- option "ICU (E2E-HOSP)"
|
||||
- option "ICU Stepdown (E2E-HOSP)"
|
||||
- option "LTACU (E2E-HOSP)"
|
||||
- option "Labor & Delivery (E2E-HOSP)"
|
||||
- option "Main OR (E2E-HOSP)"
|
||||
- option "Medical Ward (E2E-HOSP)"
|
||||
- option "NICU - Neonatal Intensive Care Unit (E2E-HOSP)"
|
||||
- option "Nursery (E2E-HOSP)"
|
||||
- option "OB Wards (E2E-HOSP)"
|
||||
- option "OPD1 (E2E-HOSP)"
|
||||
- option "OPD2 (E2E-HOSP)"
|
||||
- option "OPD3 (E2E-HOSP)"
|
||||
- option "OPD4 (E2E-HOSP)"
|
||||
- option "OPD5 (E2E-HOSP)"
|
||||
- option "OPD6 (E2E-HOSP)"
|
||||
- option "OPD7 (E2E-HOSP)"
|
||||
- option "PICU (E2E-HOSP)"
|
||||
- option "PICU Stepdown (E2E-HOSP)"
|
||||
- option "Pediatric Ward (E2E-HOSP)"
|
||||
- option "Recovery (E2E-HOSP)"
|
||||
- option "Stepdown 3-4 (E2E-HOSP)"
|
||||
- option "Surgical Wards (E2E-HOSP)"
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]: Department Category *
|
||||
- combobox [ref=e171]:
|
||||
- option "Select Category" [selected]
|
||||
- option "Medical"
|
||||
- option "Nursing"
|
||||
- option "Administrative"
|
||||
- option "Support Services"
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Department (Optional)
|
||||
- combobox "Department (Optional)" [ref=e174]:
|
||||
- option "Select Department" [selected]
|
||||
- option "Anesthesia Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Contact Center Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "Executive Administration"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Laundry Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Nursing Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Security Department"
|
||||
- option "Surgery Department"
|
||||
- generic [ref=e175]:
|
||||
- generic [ref=e176]: Section
|
||||
- combobox "Section" [ref=e177]:
|
||||
- option "Select Section" [selected]
|
||||
- generic [ref=e178]:
|
||||
- heading "Contact Information" [level=6] [ref=e179]:
|
||||
- img [ref=e180]
|
||||
- text: Contact Information
|
||||
- generic [ref=e183]:
|
||||
- generic [ref=e184]:
|
||||
- generic [ref=e185]: Contact Name *
|
||||
- textbox "Contact Name *" [ref=e186]: Test User
|
||||
- generic [ref=e187]:
|
||||
- generic [ref=e188]: Contact Phone *
|
||||
- textbox "Contact Phone *" [ref=e189]: "+966501234567"
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Contact Email
|
||||
- textbox "Contact Email" [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- heading "Inquiry Details" [level=6] [ref=e194]:
|
||||
- img [ref=e195]
|
||||
- text: Inquiry Details
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Inquiry Type *
|
||||
- combobox "Inquiry Type *" [ref=e201]:
|
||||
- option "General Inquiry" [selected]
|
||||
- option "Appointment Related"
|
||||
- option "Billing & Insurance"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Priority
|
||||
- combobox "Priority" [ref=e204]:
|
||||
- option "Low"
|
||||
- option "Medium" [selected]
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Subject *
|
||||
- textbox "Subject *" [ref=e207]:
|
||||
- /placeholder: Brief subject
|
||||
- text: Playwright Test Inquiry
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Message *
|
||||
- textbox "Message *" [ref=e210]:
|
||||
- /placeholder: Describe your inquiry
|
||||
- text: This is a test inquiry created by Playwright to verify px-employee access.
|
||||
- generic [ref=e211]:
|
||||
- button "Create Inquiry" [ref=e212] [cursor=pointer]:
|
||||
- img [ref=e213]
|
||||
- text: Create Inquiry
|
||||
- link "Cancel" [ref=e216] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- contentinfo [ref=e217]:
|
||||
- paragraph [ref=e218]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e219] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
258
.playwright-cli/page-2026-07-12T20-39-22-820Z.yml
Normal file
258
.playwright-cli/page-2026-07-12T20-39-22-820Z.yml
Normal file
@ -0,0 +1,258 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- link "Inquiries" [ref=e143] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e144]
|
||||
- generic [ref=e146]: New Inquiry
|
||||
- heading "Create New Inquiry" [level=1] [ref=e147]
|
||||
- generic [ref=e149]:
|
||||
- generic [ref=e150]:
|
||||
- img [ref=e152]
|
||||
- heading "Inquiry Information" [level=5] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- generic [ref=e157]:
|
||||
- heading "Organization" [level=6] [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: Organization
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- generic [ref=e164]: Location Type *
|
||||
- combobox "Location Type *" [active] [ref=e165]:
|
||||
- option "Select Location Type"
|
||||
- option "Outpatient"
|
||||
- option "Inpatient"
|
||||
- option "Emergency"
|
||||
- option "General"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]: Area
|
||||
- combobox "Area" [ref=e168]:
|
||||
- option "Select Area (optional)" [selected]
|
||||
- option "Anesthesia"
|
||||
- option "Dialysis"
|
||||
- option "Emergency"
|
||||
- option "Endoscopy"
|
||||
- option "ICCU"
|
||||
- option "ICU"
|
||||
- option "ICU Stepdown"
|
||||
- option "LTACU"
|
||||
- option "Labor & Delivery"
|
||||
- option "Main OR"
|
||||
- option "Medical Ward"
|
||||
- option "NICU - Neonatal Intensive Care Unit"
|
||||
- option "Nursery"
|
||||
- option "OB Wards"
|
||||
- option "OPD1"
|
||||
- option "OPD2"
|
||||
- option "OPD3"
|
||||
- option "OPD4"
|
||||
- option "OPD5"
|
||||
- option "OPD6"
|
||||
- option "OPD7"
|
||||
- option "PICU"
|
||||
- option "PICU Stepdown"
|
||||
- option "Pediatric Ward"
|
||||
- option "Recovery"
|
||||
- option "Stepdown 3-4"
|
||||
- option "Surgical Wards"
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]: Department Category *
|
||||
- combobox [ref=e171]:
|
||||
- option "Select Category" [selected]
|
||||
- option "Medical"
|
||||
- option "Nursing"
|
||||
- option "Administrative"
|
||||
- option "Support Services"
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Department (Optional)
|
||||
- combobox "Department (Optional)" [ref=e174]:
|
||||
- option "Select Department" [selected]
|
||||
- option "Anesthesia Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Contact Center Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "Executive Administration"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Laundry Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Nursing Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Security Department"
|
||||
- option "Surgery Department"
|
||||
- generic [ref=e175]:
|
||||
- generic [ref=e176]: Section
|
||||
- combobox "Section" [ref=e177]:
|
||||
- option "Select Section" [selected]
|
||||
- generic [ref=e178]:
|
||||
- heading "Contact Information" [level=6] [ref=e179]:
|
||||
- img [ref=e180]
|
||||
- text: Contact Information
|
||||
- generic [ref=e183]:
|
||||
- generic [ref=e184]:
|
||||
- generic [ref=e185]: Contact Name *
|
||||
- textbox "Contact Name *" [ref=e186]: Test User
|
||||
- generic [ref=e187]:
|
||||
- generic [ref=e188]: Contact Phone *
|
||||
- textbox "Contact Phone *" [ref=e189]: "+966501234567"
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Contact Email
|
||||
- textbox "Contact Email" [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- heading "Inquiry Details" [level=6] [ref=e194]:
|
||||
- img [ref=e195]
|
||||
- text: Inquiry Details
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Inquiry Type *
|
||||
- combobox "Inquiry Type *" [ref=e201]:
|
||||
- option "General Inquiry"
|
||||
- option "Appointment Related" [selected]
|
||||
- option "Billing & Insurance"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Priority
|
||||
- combobox "Priority" [ref=e204]:
|
||||
- option "Low"
|
||||
- option "Medium" [selected]
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Subject *
|
||||
- textbox "Subject *" [ref=e207]:
|
||||
- /placeholder: Brief subject
|
||||
- text: Playwright Test Inquiry
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Message *
|
||||
- textbox "Message *" [ref=e210]:
|
||||
- /placeholder: Describe your inquiry
|
||||
- text: This is a test inquiry created by Playwright to verify px-employee access.
|
||||
- generic [ref=e211]:
|
||||
- button "Create Inquiry" [ref=e212] [cursor=pointer]:
|
||||
- img [ref=e213]
|
||||
- text: Create Inquiry
|
||||
- link "Cancel" [ref=e216] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- contentinfo [ref=e217]:
|
||||
- paragraph [ref=e218]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e219] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
39
.playwright-cli/page-2026-07-12T20-40-03-758Z.yml
Normal file
39
.playwright-cli/page-2026-07-12T20-40-03-758Z.yml
Normal file
@ -0,0 +1,39 @@
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "HH Logo" [ref=e6]
|
||||
- heading "Welcome to PX360" [level=1] [ref=e7]
|
||||
- paragraph [ref=e8]: Patient Experience Management System
|
||||
- generic [ref=e10]:
|
||||
- generic [ref=e11]:
|
||||
- generic [ref=e12]:
|
||||
- img [ref=e13]
|
||||
- text: Email Address
|
||||
- generic [ref=e16]:
|
||||
- img [ref=e17]
|
||||
- textbox "Email Address" [active] [ref=e20]:
|
||||
- /placeholder: Enter your email
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- img [ref=e23]
|
||||
- text: Password
|
||||
- generic [ref=e26]:
|
||||
- img [ref=e27]
|
||||
- textbox "Password" [ref=e31]:
|
||||
- /placeholder: Enter your password
|
||||
- button [ref=e32] [cursor=pointer]:
|
||||
- img [ref=e33]
|
||||
- generic [ref=e36]:
|
||||
- generic [ref=e37] [cursor=pointer]:
|
||||
- checkbox "Remember me" [ref=e38]
|
||||
- generic [ref=e39]: Remember me
|
||||
- link "Forgot password?" [ref=e40] [cursor=pointer]:
|
||||
- /url: /accounts/password/reset/
|
||||
- button "Sign In" [ref=e41] [cursor=pointer]:
|
||||
- img [ref=e42]
|
||||
- text: Sign In
|
||||
- generic [ref=e45]:
|
||||
- paragraph [ref=e46]:
|
||||
- text: Secure login powered by
|
||||
- link "tenhal.sa" [ref=e47] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
- paragraph [ref=e48]: © 2026 Al Hammadi Hospital
|
||||
350
.playwright-cli/page-2026-07-12T20-40-08-887Z.yml
Normal file
350
.playwright-cli/page-2026-07-12T20-40-08-887Z.yml
Normal file
@ -0,0 +1,350 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Departments" [ref=e141] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: Contact Center Department
|
||||
- generic [ref=e147]:
|
||||
- generic [ref=e148]:
|
||||
- img [ref=e150]
|
||||
- generic [ref=e154]:
|
||||
- heading "Contact Center Department" [level=1] [ref=e155]
|
||||
- paragraph [ref=e156]: قسم مركز الاتصال
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: hh_n_contact_center_department
|
||||
- generic [ref=e162]: Administrative
|
||||
- generic [ref=e163]:
|
||||
- img [ref=e164]
|
||||
- text: E2E Test Hospital
|
||||
- generic [ref=e168]:
|
||||
- generic [ref=e170]:
|
||||
- img [ref=e172]
|
||||
- generic [ref=e177]:
|
||||
- paragraph [ref=e178]: Staff
|
||||
- paragraph [ref=e179]: "4"
|
||||
- link "Open Complaints 3 3 total" [ref=e180] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/complaints/?status=open
|
||||
- generic [ref=e181]:
|
||||
- img [ref=e183]
|
||||
- generic [ref=e186]:
|
||||
- paragraph [ref=e187]: Open Complaints
|
||||
- paragraph [ref=e188]: "3"
|
||||
- paragraph [ref=e189]: 3 total
|
||||
- link "Pending Inquiries 0 2 total" [ref=e190] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/inquiries/?status=open
|
||||
- generic [ref=e191]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e196]:
|
||||
- paragraph [ref=e197]: Pending Inquiries
|
||||
- paragraph [ref=e198]: "0"
|
||||
- paragraph [ref=e199]: 2 total
|
||||
- link "Open Observations 1 1 total" [ref=e200] [cursor=pointer]:
|
||||
- /url: /organizations/departments/d810102c-d14a-4661-8672-6ef85a77fc8f/observations/?status=open
|
||||
- generic [ref=e201]:
|
||||
- img [ref=e203]
|
||||
- generic [ref=e206]:
|
||||
- paragraph [ref=e207]: Open Observations
|
||||
- paragraph [ref=e208]: "1"
|
||||
- paragraph [ref=e209]: 1 total
|
||||
- generic [ref=e210]:
|
||||
- generic [ref=e211]:
|
||||
- img [ref=e213]
|
||||
- generic [ref=e215]:
|
||||
- heading "Pending Actions" [level=3] [ref=e216]
|
||||
- paragraph [ref=e217]: 3 items awaiting response
|
||||
- table [ref=e219]:
|
||||
- rowgroup [ref=e220]:
|
||||
- row "Type Reference Subject SLA Deadline Action" [ref=e221]:
|
||||
- columnheader "Type" [ref=e222]
|
||||
- columnheader "Reference" [ref=e223]
|
||||
- columnheader "Subject" [ref=e224]
|
||||
- columnheader "SLA Deadline" [ref=e225]
|
||||
- columnheader "Action" [ref=e226]
|
||||
- rowgroup [ref=e227]:
|
||||
- row "Complaint Response CMP-202607-0006 Test Complaint - Analysis Required - Respond" [ref=e228]:
|
||||
- cell "Complaint Response" [ref=e229]:
|
||||
- generic [ref=e230]: Complaint Response
|
||||
- cell "CMP-202607-0006" [ref=e231]
|
||||
- cell "Test Complaint - Analysis Required" [ref=e232]
|
||||
- cell "-" [ref=e233]
|
||||
- cell "Respond" [ref=e234]:
|
||||
- link "Respond" [ref=e236] [cursor=pointer]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/explain/dM0ilaUpzA1OuAceyjvwV9oxYA0geW_jsgipvrYUt_g/
|
||||
- img [ref=e237]
|
||||
- text: Respond
|
||||
- row "Complaint Explanation CMP-202607-0005 Test Complaint - No Details - Respond" [ref=e239]:
|
||||
- cell "Complaint Explanation" [ref=e240]:
|
||||
- generic [ref=e241]: Complaint Explanation
|
||||
- cell "CMP-202607-0005" [ref=e242]
|
||||
- cell "Test Complaint - No Details" [ref=e243]
|
||||
- cell "-" [ref=e244]
|
||||
- cell "Respond" [ref=e245]:
|
||||
- link "Respond" [ref=e247] [cursor=pointer]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/explain/iBIf0PrMq1RZYSEIDxSn4WZik4PFk6dunoNwarFTnXI/
|
||||
- img [ref=e248]
|
||||
- text: Respond
|
||||
- row "Complaint Explanation CMP-202607-0005 Test Complaint - No Details - Respond" [ref=e252]:
|
||||
- cell "Complaint Explanation" [ref=e253]:
|
||||
- generic [ref=e254]: Complaint Explanation
|
||||
- cell "CMP-202607-0005" [ref=e255]
|
||||
- cell "Test Complaint - No Details" [ref=e256]
|
||||
- cell "-" [ref=e257]
|
||||
- cell "Respond" [ref=e258]:
|
||||
- link "Respond" [ref=e260] [cursor=pointer]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/explain/X1Zd54Tb1NcExZeRr6ACN5RtPCwB_RTKeqNeFcVP24E/
|
||||
- img [ref=e261]
|
||||
- text: Respond
|
||||
- generic [ref=e266]:
|
||||
- button "Dashboard" [ref=e267] [cursor=pointer]:
|
||||
- img [ref=e268]
|
||||
- text: Dashboard
|
||||
- button "Staff (4)" [ref=e270] [cursor=pointer]:
|
||||
- img [ref=e271]
|
||||
- text: Staff (4)
|
||||
- button "Roles" [ref=e276] [cursor=pointer]:
|
||||
- img [ref=e277]
|
||||
- text: Roles
|
||||
- button "Sections (2)" [ref=e289] [cursor=pointer]:
|
||||
- img [ref=e290]
|
||||
- text: Sections (2)
|
||||
- button "Complaints (3)" [ref=e294] [cursor=pointer]:
|
||||
- img [ref=e295]
|
||||
- text: Complaints (3)
|
||||
- button "Inquiries (2)" [ref=e298] [cursor=pointer]:
|
||||
- img [ref=e299]
|
||||
- text: Inquiries (2)
|
||||
- button "Observations (1)" [ref=e302] [cursor=pointer]:
|
||||
- img [ref=e303]
|
||||
- text: Observations (1)
|
||||
- button "Suggestions (1)" [ref=e306] [cursor=pointer]:
|
||||
- img [ref=e307]
|
||||
- text: Suggestions (1)
|
||||
- button "Appreciations (2)" [ref=e309] [cursor=pointer]:
|
||||
- img [ref=e310]
|
||||
- text: Appreciations (2)
|
||||
- button "Standards (0)" [ref=e312] [cursor=pointer]:
|
||||
- img [ref=e313]
|
||||
- text: Standards (0)
|
||||
- button "Projects (8)" [ref=e316] [cursor=pointer]:
|
||||
- img [ref=e317]
|
||||
- text: Projects (8)
|
||||
- generic [ref=e321]:
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e323]:
|
||||
- paragraph [ref=e324]: Avg Physician Rating
|
||||
- paragraph [ref=e325]: "0.0"
|
||||
- paragraph [ref=e326]: out of 5.0
|
||||
- generic [ref=e327]:
|
||||
- paragraph [ref=e328]: Resolution Rate
|
||||
- paragraph [ref=e329]: 0%
|
||||
- paragraph [ref=e330]: 3 total
|
||||
- generic [ref=e331]:
|
||||
- paragraph [ref=e332]: Open Actions
|
||||
- paragraph [ref=e333]: "3"
|
||||
- paragraph [ref=e334]: PX actions pending
|
||||
- generic [ref=e335]:
|
||||
- paragraph [ref=e336]: Satisfaction Rate
|
||||
- paragraph [ref=e337]: 0%
|
||||
- paragraph [ref=e338]: resolution satisfaction
|
||||
- generic [ref=e339]:
|
||||
- paragraph [ref=e340]: Reopened
|
||||
- paragraph [ref=e341]: "0"
|
||||
- paragraph [ref=e342]: reopened complaints
|
||||
- generic [ref=e343]:
|
||||
- paragraph [ref=e344]: Reassigned
|
||||
- paragraph [ref=e345]: "5"
|
||||
- paragraph [ref=e346]: reassigned complaints
|
||||
- generic [ref=e347]:
|
||||
- generic [ref=e348]:
|
||||
- heading "Complaint Trend" [level=4] [ref=e349]
|
||||
- generic [ref=e351]:
|
||||
- application "area chart with 1 data series" [ref=e352]:
|
||||
- generic [ref=e355]:
|
||||
- generic "4" [ref=e356]
|
||||
- generic "3.5" [ref=e357]
|
||||
- generic "3" [ref=e358]
|
||||
- generic "2.5" [ref=e359]
|
||||
- generic "2" [ref=e360]
|
||||
- generic [ref=e361]:
|
||||
- generic [ref=e365]:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: "3"
|
||||
- generic "2026-07" [ref=e371]
|
||||
- tooltip: ●
|
||||
- generic [ref=e372]:
|
||||
- heading "Physician Rating Trend" [level=4] [ref=e373]
|
||||
- paragraph [ref=e375]: No physician rating data
|
||||
- generic [ref=e376]:
|
||||
- heading "Complaint Status" [level=4] [ref=e377]
|
||||
- generic [ref=e379]:
|
||||
- application "donut chart with 1 data series" [ref=e380]:
|
||||
- generic [ref=e386]:
|
||||
- generic:
|
||||
- generic: 100.0%
|
||||
- button "In Progress, visible. Press Enter or Space to toggle." [ref=e390] [cursor=pointer]:
|
||||
- generic [ref=e391]:
|
||||
- img
|
||||
- generic [ref=e392]: In Progress
|
||||
- text: ●
|
||||
- generic [ref=e393]:
|
||||
- heading "Actions Overview" [level=4] [ref=e394]
|
||||
- generic [ref=e396]:
|
||||
- application "donut chart with 2 data series" [ref=e397]:
|
||||
- generic [ref=e403]:
|
||||
- generic:
|
||||
- generic: 66.7%
|
||||
- generic:
|
||||
- generic: 33.3%
|
||||
- generic [ref=e406]:
|
||||
- button "In Progress, visible. Press Enter or Space to toggle." [ref=e407] [cursor=pointer]:
|
||||
- generic [ref=e408]:
|
||||
- img
|
||||
- generic [ref=e409]: In Progress
|
||||
- button "Open, visible. Press Enter or Space to toggle." [ref=e410] [cursor=pointer]:
|
||||
- generic [ref=e411]:
|
||||
- img
|
||||
- generic [ref=e412]: Open
|
||||
- text: ● ●
|
||||
- generic [ref=e413]:
|
||||
- heading "Complaint Severity" [level=4] [ref=e414]
|
||||
- generic [ref=e416]:
|
||||
- application "bar chart with 1 data series" [ref=e417]:
|
||||
- generic [ref=e420]:
|
||||
- generic "2" [ref=e421]
|
||||
- generic "1.5" [ref=e422]
|
||||
- generic "1" [ref=e423]
|
||||
- generic "0.5" [ref=e424]
|
||||
- generic "0" [ref=e425]
|
||||
- generic [ref=e426]:
|
||||
- generic [ref=e430]:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: "2"
|
||||
- generic:
|
||||
- generic: "1"
|
||||
- generic [ref=e435]:
|
||||
- generic "Low" [ref=e436]
|
||||
- generic "Medium" [ref=e437]
|
||||
- tooltip: ●
|
||||
- generic [ref=e438]:
|
||||
- heading "Satisfaction Distribution" [level=4] [ref=e439]
|
||||
- paragraph [ref=e441]: No satisfaction data
|
||||
- generic [ref=e442]:
|
||||
- heading "Top Physicians" [level=4] [ref=e443]
|
||||
- paragraph [ref=e445]: No physician data
|
||||
- generic [ref=e446]:
|
||||
- heading "Top Categories" [level=4] [ref=e447]
|
||||
- generic [ref=e449]:
|
||||
- application "bar chart with 1 data series" [ref=e450]:
|
||||
- generic [ref=e452]:
|
||||
- generic [ref=e455]:
|
||||
- generic:
|
||||
- generic:
|
||||
- generic: "3"
|
||||
- generic "Institutional Issues" [ref=e460]
|
||||
- generic [ref=e462]:
|
||||
- generic "3.0" [ref=e463]
|
||||
- generic "2.5" [ref=e464]
|
||||
- generic "2.0" [ref=e465]
|
||||
- generic "1.5" [ref=e466]
|
||||
- generic "1.0" [ref=e467]
|
||||
- generic "0.5" [ref=e468]
|
||||
- generic "0.0" [ref=e469]
|
||||
- tooltip: ●
|
||||
- contentinfo [ref=e470]:
|
||||
- paragraph [ref=e471]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e472] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
258
.playwright-cli/page-2026-07-12T20-40-21-782Z.yml
Normal file
258
.playwright-cli/page-2026-07-12T20-40-21-782Z.yml
Normal file
@ -0,0 +1,258 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Inquiries Console" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage patient inquiries and requests
|
||||
- link "New Inquiry" [ref=e144] [cursor=pointer]:
|
||||
- /url: /inquiries/new/
|
||||
- img [ref=e145]
|
||||
- text: New Inquiry
|
||||
- generic [ref=e146]:
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e150]
|
||||
- generic [ref=e153]:
|
||||
- heading "Total Inquiries" [level=6] [ref=e154]
|
||||
- heading "5" [level=2] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e159]
|
||||
- generic [ref=e162]:
|
||||
- heading "Resolved" [level=6] [ref=e163]
|
||||
- heading "2 (40.0%)" [level=2] [ref=e164]
|
||||
- generic [ref=e165]:
|
||||
- img [ref=e168]
|
||||
- generic [ref=e171]:
|
||||
- heading "In Progress" [level=6] [ref=e172]
|
||||
- heading "1" [level=2] [ref=e173]
|
||||
- generic [ref=e174]:
|
||||
- img [ref=e177]
|
||||
- generic [ref=e179]:
|
||||
- heading "Overdue" [level=6] [ref=e180]
|
||||
- heading "0" [level=2] [ref=e181]
|
||||
- generic [ref=e182]:
|
||||
- generic [ref=e183]:
|
||||
- img [ref=e185]
|
||||
- heading "Filters" [level=5] [ref=e186]
|
||||
- generic [ref=e188]:
|
||||
- generic [ref=e189]:
|
||||
- generic [ref=e190]: Search
|
||||
- textbox "Subject, contact name..." [ref=e191]
|
||||
- generic [ref=e192]:
|
||||
- generic [ref=e193]: Status
|
||||
- combobox [ref=e194]:
|
||||
- option "All Status" [selected]
|
||||
- option "Open"
|
||||
- option "In Progress"
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e195]:
|
||||
- generic [ref=e196]: Category
|
||||
- combobox [ref=e197]:
|
||||
- option "All Categories" [selected]
|
||||
- option "General"
|
||||
- option "Appointments"
|
||||
- option "Billing"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- button "Filter" [ref=e198] [cursor=pointer]:
|
||||
- img [ref=e199]
|
||||
- text: Filter
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]:
|
||||
- generic [ref=e204]:
|
||||
- img [ref=e206]
|
||||
- heading "All Inquiries" [level=5] [ref=e209]
|
||||
- generic [ref=e210]: "Total: 5"
|
||||
- paragraph [ref=e211]: "Showing: 1-5 of 5"
|
||||
- generic [ref=e212]:
|
||||
- table [ref=e213]:
|
||||
- rowgroup [ref=e214]:
|
||||
- row "Reference Subject Contact Category Status Priority Created Assigned" [ref=e215]:
|
||||
- columnheader "Reference" [ref=e216]
|
||||
- columnheader "Subject" [ref=e217]
|
||||
- columnheader "Contact" [ref=e218]
|
||||
- columnheader "Category" [ref=e219]
|
||||
- columnheader "Status" [ref=e220]
|
||||
- columnheader "Priority" [ref=e221]
|
||||
- columnheader "Created" [ref=e222]
|
||||
- columnheader "Assigned" [ref=e223]
|
||||
- rowgroup [ref=e224]:
|
||||
- row "INQ-202607-0007 test12121212 ismail mosa 0569874563 General Information Open Medium Jul 12, 2026 Unassigned" [ref=e225] [cursor=pointer]:
|
||||
- cell "INQ-202607-0007" [ref=e226]:
|
||||
- code [ref=e227]: INQ-202607-0007
|
||||
- cell "test12121212" [ref=e228]:
|
||||
- link "test12121212" [ref=e229]:
|
||||
- /url: /inquiries/b4a65498-e8d8-403c-82bb-ae09adc7fc1c/
|
||||
- cell "ismail mosa 0569874563" [ref=e230]:
|
||||
- generic [ref=e231]: ismail mosa
|
||||
- generic [ref=e232]: "0569874563"
|
||||
- cell "General Information" [ref=e233]
|
||||
- cell "Open" [ref=e234]
|
||||
- cell "Medium" [ref=e235]
|
||||
- cell "Jul 12, 2026" [ref=e236]
|
||||
- cell "Unassigned" [ref=e237]
|
||||
- row "INQ-202607-0006 test12121212 test 0569874563 General Information Open Medium Jul 12, 2026 Unassigned" [ref=e238] [cursor=pointer]:
|
||||
- cell "INQ-202607-0006" [ref=e239]:
|
||||
- code [ref=e240]: INQ-202607-0006
|
||||
- cell "test12121212" [ref=e241]:
|
||||
- link "test12121212" [ref=e242]:
|
||||
- /url: /inquiries/1c3eda08-ff03-4521-bcb8-ec137c79b417/
|
||||
- cell "test 0569874563" [ref=e243]:
|
||||
- generic [ref=e244]: test
|
||||
- generic [ref=e245]: "0569874563"
|
||||
- cell "General Information" [ref=e246]
|
||||
- cell "Open" [ref=e247]
|
||||
- cell "Medium" [ref=e248]
|
||||
- cell "Jul 12, 2026" [ref=e249]
|
||||
- cell "Unassigned" [ref=e250]
|
||||
- row "INQ-202607-0005 test test 0569874589 General Information In Progress Medium Jul 09, 2026 Unassigned" [ref=e251] [cursor=pointer]:
|
||||
- cell "INQ-202607-0005" [ref=e252]:
|
||||
- code [ref=e253]: INQ-202607-0005
|
||||
- cell "test" [ref=e254]:
|
||||
- link "test" [ref=e255]:
|
||||
- /url: /inquiries/c05309c2-3518-46ae-9b1c-c144acdb5d58/
|
||||
- cell "test 0569874589" [ref=e256]:
|
||||
- generic [ref=e257]: test
|
||||
- generic [ref=e258]: "0569874589"
|
||||
- cell "General Information" [ref=e259]
|
||||
- cell "In Progress" [ref=e260]
|
||||
- cell "Medium" [ref=e261]
|
||||
- cell "Jul 09, 2026" [ref=e262]
|
||||
- cell "Unassigned" [ref=e263]
|
||||
- row "INQ-202607-0004 hi ismail 0569874589 General Information Resolved Medium Jul 09, 2026 ismail mosa" [ref=e264] [cursor=pointer]:
|
||||
- cell "INQ-202607-0004" [ref=e265]:
|
||||
- code [ref=e266]: INQ-202607-0004
|
||||
- cell "hi" [ref=e267]:
|
||||
- link "hi" [ref=e268]:
|
||||
- /url: /inquiries/d1df0b83-5c16-4f51-b735-e89f612deb50/
|
||||
- cell "ismail 0569874589" [ref=e269]:
|
||||
- generic [ref=e270]: ismail
|
||||
- generic [ref=e271]: "0569874589"
|
||||
- cell "General Information" [ref=e272]
|
||||
- cell "Resolved" [ref=e273]
|
||||
- cell "Medium" [ref=e274]
|
||||
- cell "Jul 09, 2026" [ref=e275]
|
||||
- cell "ismail mosa" [ref=e276]
|
||||
- row "INQ-202607-0003 test test 0569874589 General Information Resolved Medium Jul 08, 2026 ismail mosa" [ref=e277] [cursor=pointer]:
|
||||
- cell "INQ-202607-0003" [ref=e278]:
|
||||
- code [ref=e279]: INQ-202607-0003
|
||||
- cell "test" [ref=e280]:
|
||||
- link "test" [ref=e281]:
|
||||
- /url: /inquiries/031cbd06-e2dc-4cd9-9b81-b1218e090d6c/
|
||||
- cell "test 0569874589" [ref=e282]:
|
||||
- generic [ref=e283]: test
|
||||
- generic [ref=e284]: "0569874589"
|
||||
- cell "General Information" [ref=e285]
|
||||
- cell "Resolved" [ref=e286]
|
||||
- cell "Medium" [ref=e287]
|
||||
- cell "Jul 08, 2026" [ref=e288]
|
||||
- cell "ismail mosa" [ref=e289]
|
||||
- generic [ref=e290]:
|
||||
- generic [ref=e291]:
|
||||
- generic [ref=e292]: Showing 1-5 of 5
|
||||
- generic [ref=e293]:
|
||||
- generic [ref=e294]: Show
|
||||
- combobox [ref=e295]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e296]:
|
||||
- img [ref=e298]
|
||||
- generic [ref=e300]: "1"
|
||||
- img [ref=e302]
|
||||
- contentinfo [ref=e304]:
|
||||
- paragraph [ref=e305]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e306] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
243
.playwright-cli/page-2026-07-12T20-40-22-923Z.yml
Normal file
243
.playwright-cli/page-2026-07-12T20-40-22-923Z.yml
Normal file
@ -0,0 +1,243 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Complaints Registry" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage and monitor patient feedback in real-time
|
||||
- generic [ref=e144]:
|
||||
- generic [ref=e145]:
|
||||
- img [ref=e146]
|
||||
- textbox "Search ID, Name or Dept..." [ref=e149]
|
||||
- link "Public Form" [ref=e150] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e151]
|
||||
- text: Public Form
|
||||
- button "Export" [ref=e155] [cursor=pointer]:
|
||||
- img [ref=e156]
|
||||
- text: Export
|
||||
- link "New Case" [ref=e159] [cursor=pointer]:
|
||||
- /url: /complaints/new/
|
||||
- img [ref=e160]
|
||||
- text: New Case
|
||||
- generic [ref=e161]:
|
||||
- generic [ref=e162]:
|
||||
- img [ref=e165]
|
||||
- generic [ref=e169]:
|
||||
- heading "Total Received" [level=6] [ref=e170]
|
||||
- heading "3" [level=2] [ref=e171]
|
||||
- generic [ref=e172]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- heading "Resolved" [level=6] [ref=e179]
|
||||
- heading "0 (0.0%)" [level=2] [ref=e180]
|
||||
- generic [ref=e181]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e187]:
|
||||
- heading "Pending" [level=6] [ref=e188]
|
||||
- heading "3 (100.0%)" [level=2] [ref=e189]
|
||||
- generic [ref=e190]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e195]:
|
||||
- heading "TAT Alert" [level=6] [ref=e196]
|
||||
- heading "3 (100.0%)" [level=2] [ref=e197]
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]:
|
||||
- img [ref=e202]
|
||||
- heading "Complaints" [level=5] [ref=e204]
|
||||
- generic [ref=e205]:
|
||||
- link "All Cases" [ref=e206] [cursor=pointer]:
|
||||
- /url: "?"
|
||||
- link "Pending" [ref=e207] [cursor=pointer]:
|
||||
- /url: "?status=open"
|
||||
- link "In Progress" [ref=e208] [cursor=pointer]:
|
||||
- /url: "?status=in_progress"
|
||||
- link "Resolved" [ref=e209] [cursor=pointer]:
|
||||
- /url: "?status=resolved"
|
||||
- button "Advanced" [ref=e211] [cursor=pointer]:
|
||||
- img [ref=e212]
|
||||
- text: Advanced
|
||||
- paragraph [ref=e213]: "Showing: 1-3 of 3"
|
||||
- generic [ref=e214]:
|
||||
- table [ref=e215]:
|
||||
- rowgroup [ref=e216]:
|
||||
- row "Complaint ID Patient Name Description Source Department Status SLA Severity Priority Assigned Actions" [ref=e217]:
|
||||
- columnheader "Complaint ID" [ref=e218]
|
||||
- columnheader "Patient Name" [ref=e219]
|
||||
- columnheader "Description" [ref=e220]
|
||||
- columnheader "Source" [ref=e221]
|
||||
- columnheader "Department" [ref=e222]
|
||||
- columnheader "Status" [ref=e223]
|
||||
- columnheader "SLA" [ref=e224]
|
||||
- columnheader "Severity" [ref=e225]
|
||||
- columnheader "Priority" [ref=e226]
|
||||
- columnheader "Assigned" [ref=e227]
|
||||
- columnheader "Actions" [ref=e228]
|
||||
- rowgroup [ref=e229]:
|
||||
- row "CMP-202607-0006 test شكوى عامة Patient Patient Experience Department In Progress Overdue 21h 46m Medium Medium test test" [ref=e230] [cursor=pointer]:
|
||||
- cell "CMP-202607-0006" [ref=e231]
|
||||
- cell "test" [ref=e232]:
|
||||
- generic [ref=e233]: test
|
||||
- cell "شكوى عامة" [ref=e234]:
|
||||
- generic [ref=e235]: شكوى عامة
|
||||
- button "View description" [ref=e236]:
|
||||
- img [ref=e237]
|
||||
- cell "Patient" [ref=e246]
|
||||
- cell "Patient Experience Department" [ref=e247]
|
||||
- cell "In Progress" [ref=e248]
|
||||
- cell "Overdue 21h 46m" [ref=e249]
|
||||
- cell "Medium" [ref=e250]
|
||||
- cell "Medium" [ref=e251]
|
||||
- cell "test test" [ref=e252]
|
||||
- cell [ref=e253]:
|
||||
- generic [ref=e254]:
|
||||
- link "View" [ref=e255]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/
|
||||
- img [ref=e256]
|
||||
- link "Assign" [ref=e259]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/assign/
|
||||
- img [ref=e260]
|
||||
- row "CMP-202607-0005 ismail شكوى اختبار Patient Contact Center Department In Progress Overdue 23h 22m Low Low test test" [ref=e263] [cursor=pointer]:
|
||||
- cell "CMP-202607-0005" [ref=e264]
|
||||
- cell "ismail" [ref=e265]:
|
||||
- generic [ref=e266]: ismail
|
||||
- cell "شكوى اختبار" [ref=e267]:
|
||||
- generic [ref=e268]: شكوى اختبار
|
||||
- button "View description" [ref=e269]:
|
||||
- img [ref=e270]
|
||||
- cell "Patient" [ref=e279]
|
||||
- cell "Contact Center Department" [ref=e280]
|
||||
- cell "In Progress" [ref=e281]
|
||||
- cell "Overdue 23h 22m" [ref=e282]
|
||||
- cell "Low" [ref=e283]
|
||||
- cell "Low" [ref=e284]
|
||||
- cell "test test" [ref=e285]
|
||||
- cell [ref=e286]:
|
||||
- link "View" [ref=e288]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/
|
||||
- img [ref=e289]
|
||||
- row "CMP-202607-0004 OVR ismail اختبار عام Internal Contact Center Department In Progress Overdue 4d 7h 22m Low Low ismail mosa" [ref=e292] [cursor=pointer]:
|
||||
- cell "CMP-202607-0004 OVR" [ref=e293]
|
||||
- cell "ismail" [ref=e294]:
|
||||
- generic [ref=e295]: ismail
|
||||
- cell "اختبار عام" [ref=e296]:
|
||||
- generic [ref=e297]: اختبار عام
|
||||
- button "View description" [ref=e298]:
|
||||
- img [ref=e299]
|
||||
- cell "Internal" [ref=e308]
|
||||
- cell "Contact Center Department" [ref=e309]
|
||||
- cell "In Progress" [ref=e310]
|
||||
- cell "Overdue 4d 7h 22m" [ref=e311]
|
||||
- cell "Low" [ref=e312]
|
||||
- cell "Low" [ref=e313]
|
||||
- cell "ismail mosa" [ref=e314]
|
||||
- cell [ref=e315]:
|
||||
- link "View" [ref=e317]:
|
||||
- /url: /complaints/1c4e8dde-48d8-42ab-8c8d-4ada8314fc40/
|
||||
- img [ref=e318]
|
||||
- generic [ref=e321]:
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e323]: Showing 1-3 of 3
|
||||
- generic [ref=e324]:
|
||||
- generic [ref=e325]: Show
|
||||
- combobox [ref=e326]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e327]:
|
||||
- img [ref=e329]
|
||||
- generic [ref=e331]: "1"
|
||||
- img [ref=e333]
|
||||
- contentinfo [ref=e335]:
|
||||
- paragraph [ref=e336]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e337] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
344
.playwright-cli/page-2026-07-12T20-40-24-046Z.yml
Normal file
344
.playwright-cli/page-2026-07-12T20-40-24-046Z.yml
Normal file
@ -0,0 +1,344 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Observations" [level=1] [ref=e142]:
|
||||
- img [ref=e143]
|
||||
- text: Observations
|
||||
- paragraph [ref=e146]: Manage and triage staff-reported observations
|
||||
- generic [ref=e147]:
|
||||
- link "New Observation" [ref=e148] [cursor=pointer]:
|
||||
- /url: /observations/create/
|
||||
- img [ref=e149]
|
||||
- text: New Observation
|
||||
- link "Public Form" [ref=e151] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e152]
|
||||
- text: Public Form
|
||||
- generic [ref=e153]:
|
||||
- generic [ref=e155]:
|
||||
- generic [ref=e156]:
|
||||
- paragraph [ref=e157]: Total
|
||||
- paragraph [ref=e158]: "2"
|
||||
- img [ref=e160]
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- paragraph [ref=e164]: New
|
||||
- paragraph
|
||||
- img [ref=e166]
|
||||
- generic [ref=e170]:
|
||||
- generic [ref=e171]:
|
||||
- paragraph [ref=e172]: In Progress
|
||||
- paragraph [ref=e173]: "2"
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- generic [ref=e179]:
|
||||
- img [ref=e181]
|
||||
- heading "Filters" [level=5] [ref=e183]
|
||||
- button [ref=e184] [cursor=pointer]:
|
||||
- img [ref=e185]
|
||||
- generic [ref=e188]:
|
||||
- generic [ref=e189]:
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Search
|
||||
- textbox "Tracking code, description..." [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- generic [ref=e194]: Status
|
||||
- combobox [ref=e195]:
|
||||
- option "All Statuses" [selected]
|
||||
- option "Open"
|
||||
- option "In Progress"
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e196]:
|
||||
- generic [ref=e197]: Severity
|
||||
- combobox [ref=e198]:
|
||||
- option "All Severities" [selected]
|
||||
- option "Low"
|
||||
- option "Medium"
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Category
|
||||
- combobox [ref=e201]:
|
||||
- option "All Categories" [selected]
|
||||
- option "Patient Safety"
|
||||
- option "Clinical Quality"
|
||||
- option "Infection Control"
|
||||
- option "Medication Safety"
|
||||
- option "Equipment & Devices"
|
||||
- option "Facility & Environment"
|
||||
- option "Staff Behavior"
|
||||
- option "Communication"
|
||||
- option "Documentation"
|
||||
- option "Process & Workflow"
|
||||
- option "Security"
|
||||
- option "IT & Systems"
|
||||
- option "Housekeeping"
|
||||
- option "Food Services"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Department
|
||||
- combobox [ref=e204]:
|
||||
- option "All Departments" [selected]
|
||||
- option "Contact Center Department"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Executive Administration"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Security Department"
|
||||
- option "Anesthesia Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Surgery Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Nursing Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Laundry Department"
|
||||
- generic [ref=e206] [cursor=pointer]:
|
||||
- generic [ref=e207]: All Departments
|
||||
- combobox "All Departments" [ref=e208]
|
||||
- generic [ref=e209]:
|
||||
- generic [ref=e210]: Assigned To
|
||||
- combobox [ref=e211]:
|
||||
- option "All Users" [selected]
|
||||
- option "E2E PX Employee"
|
||||
- option "test test"
|
||||
- option "test test"
|
||||
- generic [ref=e212]:
|
||||
- generic [ref=e213]: Date From
|
||||
- textbox [ref=e214]
|
||||
- generic [ref=e215]:
|
||||
- generic [ref=e216]: Date To
|
||||
- textbox [ref=e217]
|
||||
- generic [ref=e218]:
|
||||
- button "Apply Filters" [ref=e219] [cursor=pointer]:
|
||||
- img [ref=e220]
|
||||
- text: Apply Filters
|
||||
- link "Clear" [ref=e223] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e224]
|
||||
- text: Clear
|
||||
- generic [ref=e229]:
|
||||
- img [ref=e231]
|
||||
- heading "Observations List" [level=5] [ref=e233]
|
||||
- generic [ref=e234]: Showing 1 to 4 of 4 observations
|
||||
- table [ref=e237]:
|
||||
- rowgroup [ref=e238]:
|
||||
- row "Reference Date Category Description Severity Status Reporter Department Assigned Actions" [ref=e239]:
|
||||
- columnheader "Reference" [ref=e240]
|
||||
- columnheader "Date" [ref=e241]
|
||||
- columnheader "Category" [ref=e242]
|
||||
- columnheader "Description" [ref=e243]
|
||||
- columnheader "Severity" [ref=e244]
|
||||
- columnheader "Status" [ref=e245]
|
||||
- columnheader "Reporter" [ref=e246]
|
||||
- columnheader "Department" [ref=e247]
|
||||
- columnheader "Assigned" [ref=e248]
|
||||
- columnheader "Actions" [ref=e249]
|
||||
- rowgroup [ref=e250]:
|
||||
- row "OBS-202607-0001 Jul 08, 2026 16:33 - test observation Medium In Progress ismail mosa Contact Center Department ismail mosa" [ref=e251]:
|
||||
- cell "OBS-202607-0001" [ref=e252]
|
||||
- cell "Jul 08, 2026 16:33" [ref=e253]:
|
||||
- text: Jul 08, 2026
|
||||
- text: 16:33
|
||||
- cell "-" [ref=e254]
|
||||
- cell "test observation" [ref=e255]:
|
||||
- generic [ref=e256]: test observation
|
||||
- cell "Medium" [ref=e257]
|
||||
- cell "In Progress" [ref=e258]
|
||||
- cell "ismail mosa" [ref=e259]
|
||||
- cell "Contact Center Department" [ref=e260]
|
||||
- cell "ismail mosa" [ref=e261]
|
||||
- cell [ref=e262]:
|
||||
- link [ref=e263] [cursor=pointer]:
|
||||
- /url: /observations/1d925706-110e-4bad-808f-26aae2029144/
|
||||
- img [ref=e264]
|
||||
- row "OBS-202606-E2EHOSP-0056 Jun 22, 2026 10:16 Other Observation i notice some trash in the hall Medium In Progress E2E PX Admin Contact Center Department E2E PX Admin" [ref=e267]:
|
||||
- cell "OBS-202606-E2EHOSP-0056" [ref=e268]
|
||||
- cell "Jun 22, 2026 10:16" [ref=e269]:
|
||||
- text: Jun 22, 2026
|
||||
- text: 10:16
|
||||
- cell "Other" [ref=e270]:
|
||||
- generic [ref=e271]: Other
|
||||
- cell "Observation i notice some trash in the hall" [ref=e272]:
|
||||
- generic [ref=e273]:
|
||||
- strong [ref=e274]: Observation
|
||||
- text: i notice some trash in the hall
|
||||
- cell "Medium" [ref=e275]
|
||||
- cell "In Progress" [ref=e276]
|
||||
- cell "E2E PX Admin" [ref=e277]
|
||||
- cell "Contact Center Department" [ref=e278]
|
||||
- cell "E2E PX Admin" [ref=e279]
|
||||
- cell [ref=e280]:
|
||||
- link [ref=e281] [cursor=pointer]:
|
||||
- /url: /observations/b9a80c99-31dc-4cda-a1e3-c20d94a28729/
|
||||
- img [ref=e282]
|
||||
- row "OBS-O8TCIC Jun 09, 2026 13:36 - Wet floor near the main entrance, no warning sign posted. Medium In Progress Anonymous Reporter - ismail mosa" [ref=e285]:
|
||||
- cell "OBS-O8TCIC" [ref=e286]
|
||||
- cell "Jun 09, 2026 13:36" [ref=e287]:
|
||||
- text: Jun 09, 2026
|
||||
- text: 13:36
|
||||
- cell "-" [ref=e288]
|
||||
- cell "Wet floor near the main entrance, no warning sign posted." [ref=e289]:
|
||||
- generic [ref=e290]: Wet floor near the main entrance, no warning sign posted.
|
||||
- cell "Medium" [ref=e291]
|
||||
- cell "In Progress" [ref=e292]
|
||||
- cell "Anonymous Reporter" [ref=e293]
|
||||
- cell "-" [ref=e294]
|
||||
- cell "ismail mosa" [ref=e295]
|
||||
- cell [ref=e296]:
|
||||
- link [ref=e297] [cursor=pointer]:
|
||||
- /url: /observations/5f8fc6c6-3b38-4b88-8144-3f926c24e290/
|
||||
- img [ref=e298]
|
||||
- row "OBS-QPE02F Jun 07, 2026 21:08 Other Observation safsafsad Medium Open ismail Daif - Unassigned" [ref=e301]:
|
||||
- cell "OBS-QPE02F" [ref=e302]
|
||||
- cell "Jun 07, 2026 21:08" [ref=e303]:
|
||||
- text: Jun 07, 2026
|
||||
- text: 21:08
|
||||
- cell "Other" [ref=e304]:
|
||||
- generic [ref=e305]: Other
|
||||
- cell "Observation safsafsad" [ref=e306]:
|
||||
- generic [ref=e307]:
|
||||
- strong [ref=e308]: Observation
|
||||
- text: safsafsad
|
||||
- cell "Medium" [ref=e309]
|
||||
- cell "Open" [ref=e310]
|
||||
- cell "ismail Daif" [ref=e311]
|
||||
- cell "-" [ref=e312]
|
||||
- cell "Unassigned" [ref=e313]
|
||||
- cell [ref=e314]:
|
||||
- link [ref=e315] [cursor=pointer]:
|
||||
- /url: /observations/327d9b79-2126-4800-8cd7-647f006271ce/
|
||||
- img [ref=e316]
|
||||
- generic [ref=e319]:
|
||||
- generic [ref=e320]:
|
||||
- generic [ref=e321]: Showing 1-4 of 4
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e323]: Show
|
||||
- combobox [ref=e324]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e325]:
|
||||
- img [ref=e327]
|
||||
- generic [ref=e329]: "1"
|
||||
- img [ref=e331]
|
||||
- contentinfo [ref=e333]:
|
||||
- paragraph [ref=e334]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e335] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
201
.playwright-cli/page-2026-07-12T20-40-25-079Z.yml
Normal file
201
.playwright-cli/page-2026-07-12T20-40-25-079Z.yml
Normal file
@ -0,0 +1,201 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Suggestions" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage and review submitted suggestions
|
||||
- link "New Suggestion" [ref=e145] [cursor=pointer]:
|
||||
- /url: /suggestions/create/
|
||||
- img [ref=e146]
|
||||
- text: New Suggestion
|
||||
- generic [ref=e147]:
|
||||
- generic [ref=e148]:
|
||||
- img [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- heading "Total Suggestions" [level=6] [ref=e154]
|
||||
- heading "3" [level=2] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e159]
|
||||
- generic [ref=e161]:
|
||||
- heading "Compliments" [level=6] [ref=e162]
|
||||
- heading "0" [level=2] [ref=e163]
|
||||
- generic [ref=e164]:
|
||||
- img [ref=e167]
|
||||
- generic [ref=e169]:
|
||||
- heading "Avg Rating" [level=6] [ref=e170]
|
||||
- heading "0.0 /5" [level=2] [ref=e171]
|
||||
- generic [ref=e172]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- heading "Pending Review" [level=6] [ref=e179]
|
||||
- heading "2" [level=2] [ref=e180]
|
||||
- generic [ref=e181]:
|
||||
- generic [ref=e182]:
|
||||
- generic [ref=e183]:
|
||||
- img [ref=e185]
|
||||
- heading "Suggestions" [level=5] [ref=e187]
|
||||
- generic [ref=e188]:
|
||||
- link "All" [ref=e189] [cursor=pointer]:
|
||||
- /url: "?"
|
||||
- link "Suggestions" [ref=e190] [cursor=pointer]:
|
||||
- /url: "?feedback_type=suggestion"
|
||||
- link "Compliments" [ref=e191] [cursor=pointer]:
|
||||
- /url: "?feedback_type=compliment"
|
||||
- button "Advanced" [ref=e193] [cursor=pointer]:
|
||||
- img [ref=e194]
|
||||
- text: Advanced
|
||||
- paragraph [ref=e195]: "Showing: 1-3 of 3"
|
||||
- table [ref=e197]:
|
||||
- rowgroup [ref=e198]:
|
||||
- row "ID Type Contact Title Category Rating Sentiment Status Created Actions" [ref=e199]:
|
||||
- columnheader "ID" [ref=e200]
|
||||
- columnheader "Type" [ref=e201]
|
||||
- columnheader "Contact" [ref=e202]
|
||||
- columnheader "Title" [ref=e203]
|
||||
- columnheader "Category" [ref=e204]
|
||||
- columnheader "Rating" [ref=e205]
|
||||
- columnheader "Sentiment" [ref=e206]
|
||||
- columnheader "Status" [ref=e207]
|
||||
- columnheader "Created" [ref=e208]
|
||||
- columnheader "Actions" [ref=e209]
|
||||
- rowgroup [ref=e210]:
|
||||
- row "SGT-202607-0003 Suggestion ismail Daif t Other - Neutral Submitted Jul 09, 2026" [ref=e211] [cursor=pointer]:
|
||||
- cell "SGT-202607-0003" [ref=e212]
|
||||
- cell "Suggestion" [ref=e213]
|
||||
- cell "ismail Daif" [ref=e214]:
|
||||
- generic [ref=e215]: ismail Daif
|
||||
- cell "t" [ref=e216]:
|
||||
- generic [ref=e217]: t
|
||||
- cell "Other" [ref=e218]
|
||||
- cell "-" [ref=e219]
|
||||
- cell "Neutral" [ref=e220]
|
||||
- cell "Submitted" [ref=e221]
|
||||
- cell "Jul 09, 2026" [ref=e222]
|
||||
- cell [ref=e223]:
|
||||
- link "View" [ref=e225]:
|
||||
- /url: /suggestions/78f015c5-fbed-4e10-987f-aa8ced75545a/
|
||||
- img [ref=e226]
|
||||
- row "SGT-202607-0002 Suggestion ismail test Other - Neutral Submitted Jul 09, 2026" [ref=e229] [cursor=pointer]:
|
||||
- cell "SGT-202607-0002" [ref=e230]
|
||||
- cell "Suggestion" [ref=e231]
|
||||
- cell "ismail" [ref=e232]:
|
||||
- generic [ref=e233]: ismail
|
||||
- cell "test" [ref=e234]:
|
||||
- generic [ref=e235]: test
|
||||
- cell "Other" [ref=e236]
|
||||
- cell "-" [ref=e237]
|
||||
- cell "Neutral" [ref=e238]
|
||||
- cell "Submitted" [ref=e239]
|
||||
- cell "Jul 09, 2026" [ref=e240]
|
||||
- cell [ref=e241]:
|
||||
- link "View" [ref=e243]:
|
||||
- /url: /suggestions/04b009f3-53aa-4eac-9baf-39b3633e56b0/
|
||||
- img [ref=e244]
|
||||
- row "SGT-202607-0001 Suggestion ismail The suggestion titled 'test' with content 'test' appears … Other - Neutral Closed Jul 09, 2026" [ref=e247] [cursor=pointer]:
|
||||
- cell "SGT-202607-0001" [ref=e248]
|
||||
- cell "Suggestion" [ref=e249]
|
||||
- cell "ismail" [ref=e250]:
|
||||
- generic [ref=e251]: ismail
|
||||
- cell "The suggestion titled 'test' with content 'test' appears …" [ref=e252]:
|
||||
- generic [ref=e253]: The suggestion titled 'test' with content 'test' appears …
|
||||
- cell "Other" [ref=e254]
|
||||
- cell "-" [ref=e255]
|
||||
- cell "Neutral" [ref=e256]
|
||||
- cell "Closed" [ref=e257]
|
||||
- cell "Jul 09, 2026" [ref=e258]
|
||||
- cell [ref=e259]:
|
||||
- link "View" [ref=e261]:
|
||||
- /url: /suggestions/ef1e633b-7fae-46e2-baab-0d5bdd71e7b7/
|
||||
- img [ref=e262]
|
||||
- contentinfo [ref=e265]:
|
||||
- paragraph [ref=e266]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e267] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
221
.playwright-cli/page-2026-07-12T20-40-26-032Z.yml
Normal file
221
.playwright-cli/page-2026-07-12T20-40-26-032Z.yml
Normal file
@ -0,0 +1,221 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Appreciations" [level=1] [ref=e142]:
|
||||
- img [ref=e143]
|
||||
- text: Appreciations
|
||||
- paragraph [ref=e145]: Manage and review patient appreciation submissions
|
||||
- generic [ref=e146]:
|
||||
- link "Create Internal" [ref=e147] [cursor=pointer]:
|
||||
- /url: /appreciation/new/
|
||||
- img [ref=e148]
|
||||
- text: Create Internal
|
||||
- link "Public Form" [ref=e149] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e150]
|
||||
- text: Public Form
|
||||
- generic [ref=e154]:
|
||||
- paragraph [ref=e155]: "0"
|
||||
- paragraph [ref=e156]: Pending
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e159]:
|
||||
- img [ref=e161]
|
||||
- generic [ref=e164]:
|
||||
- paragraph [ref=e165]: Total
|
||||
- paragraph [ref=e166]: "3"
|
||||
- generic [ref=e168]:
|
||||
- img [ref=e170]
|
||||
- generic [ref=e173]:
|
||||
- paragraph [ref=e174]: Draft
|
||||
- paragraph [ref=e175]: "0"
|
||||
- generic [ref=e177]:
|
||||
- img [ref=e179]
|
||||
- generic [ref=e181]:
|
||||
- paragraph [ref=e182]: Activated
|
||||
- paragraph [ref=e183]: "0"
|
||||
- generic [ref=e185]:
|
||||
- img [ref=e187]
|
||||
- generic [ref=e190]:
|
||||
- paragraph [ref=e191]: Sent
|
||||
- paragraph [ref=e192]: "3"
|
||||
- generic [ref=e193]:
|
||||
- link "Leaderboard See top performers" [ref=e194] [cursor=pointer]:
|
||||
- /url: /appreciation/leaderboard/
|
||||
- generic [ref=e196]:
|
||||
- img [ref=e198]
|
||||
- generic [ref=e204]:
|
||||
- heading "Leaderboard" [level=3] [ref=e205]
|
||||
- paragraph [ref=e206]: See top performers
|
||||
- link "My Badges View earned badges" [ref=e207] [cursor=pointer]:
|
||||
- /url: /appreciation/badges/
|
||||
- generic [ref=e209]:
|
||||
- img [ref=e211]
|
||||
- generic [ref=e214]:
|
||||
- heading "My Badges" [level=3] [ref=e215]
|
||||
- paragraph [ref=e216]: View earned badges
|
||||
- generic [ref=e217]:
|
||||
- generic [ref=e218]:
|
||||
- img [ref=e220]
|
||||
- heading "Filters" [level=3] [ref=e222]
|
||||
- generic [ref=e224]:
|
||||
- generic [ref=e225]:
|
||||
- generic [ref=e226]: Search
|
||||
- textbox "Search messages..." [ref=e227]
|
||||
- generic [ref=e228]:
|
||||
- generic [ref=e229]: Status
|
||||
- combobox [ref=e230]:
|
||||
- option "All Status" [selected]
|
||||
- option "Draft"
|
||||
- option "Activated"
|
||||
- option "AI Analyzed"
|
||||
- option "Sent"
|
||||
- option "Acknowledged"
|
||||
- button "Search" [ref=e231] [cursor=pointer]:
|
||||
- img [ref=e232]
|
||||
- text: Search
|
||||
- link "Clear" [ref=e235] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- table [ref=e237]:
|
||||
- rowgroup [ref=e238]:
|
||||
- row "From Message Staff Department Category Status Date Actions" [ref=e239]:
|
||||
- columnheader "From" [ref=e240]
|
||||
- columnheader "Message" [ref=e241]
|
||||
- columnheader "Staff" [ref=e242]
|
||||
- columnheader "Department" [ref=e243]
|
||||
- columnheader "Category" [ref=e244]
|
||||
- columnheader "Status" [ref=e245]
|
||||
- columnheader "Date" [ref=e246]
|
||||
- columnheader "Actions" [ref=e247]
|
||||
- rowgroup [ref=e248]:
|
||||
- row "ismail thank - Contact Center Department - Sent Jul 09, 2026" [ref=e249] [cursor=pointer]:
|
||||
- cell "ismail" [ref=e250]
|
||||
- cell "thank" [ref=e251]:
|
||||
- generic [ref=e252]: thank
|
||||
- cell "-" [ref=e253]
|
||||
- cell "Contact Center Department" [ref=e254]
|
||||
- cell "-" [ref=e255]
|
||||
- cell "Sent" [ref=e256]
|
||||
- cell "Jul 09, 2026" [ref=e257]
|
||||
- cell [ref=e258]:
|
||||
- link "View" [ref=e260]:
|
||||
- /url: /appreciation/detail/5306821e-d596-498f-88c5-fa3addcdd491/
|
||||
- img [ref=e261]
|
||||
- row "ismail mosa test - Contact Center Department - Sent Jul 09, 2026" [ref=e264] [cursor=pointer]:
|
||||
- cell "ismail mosa" [ref=e265]
|
||||
- cell "test" [ref=e266]:
|
||||
- generic [ref=e267]: test
|
||||
- cell "-" [ref=e268]
|
||||
- cell "Contact Center Department" [ref=e269]
|
||||
- cell "-" [ref=e270]
|
||||
- cell "Sent" [ref=e271]
|
||||
- cell "Jul 09, 2026" [ref=e272]
|
||||
- cell [ref=e273]:
|
||||
- link "View" [ref=e275]:
|
||||
- /url: /appreciation/detail/3697b78b-5ff3-4392-8982-af20f58daebf/
|
||||
- img [ref=e276]
|
||||
- row "ismail mosa thank you - Contact Center Department - Sent Jul 09, 2026" [ref=e279] [cursor=pointer]:
|
||||
- cell "ismail mosa" [ref=e280]
|
||||
- cell "thank you" [ref=e281]:
|
||||
- generic [ref=e282]: thank you
|
||||
- cell "-" [ref=e283]
|
||||
- cell "Contact Center Department" [ref=e284]
|
||||
- cell "-" [ref=e285]
|
||||
- cell "Sent" [ref=e286]
|
||||
- cell "Jul 09, 2026" [ref=e287]
|
||||
- cell [ref=e288]:
|
||||
- link "View" [ref=e290]:
|
||||
- /url: /appreciation/detail/240ea5bd-a12d-4f76-8a79-a8dd9de5714a/
|
||||
- img [ref=e291]
|
||||
- contentinfo [ref=e294]:
|
||||
- paragraph [ref=e295]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e296] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
256
.playwright-cli/page-2026-07-12T20-40-27-116Z.yml
Normal file
256
.playwright-cli/page-2026-07-12T20-40-27-116Z.yml
Normal file
@ -0,0 +1,256 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- link "Inquiries" [ref=e143] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e144]
|
||||
- generic [ref=e146]: New Inquiry
|
||||
- heading "Create New Inquiry" [level=1] [ref=e147]
|
||||
- generic [ref=e149]:
|
||||
- generic [ref=e150]:
|
||||
- img [ref=e152]
|
||||
- heading "Inquiry Information" [level=5] [ref=e155]
|
||||
- generic [ref=e156]:
|
||||
- generic [ref=e157]:
|
||||
- heading "Organization" [level=6] [ref=e158]:
|
||||
- img [ref=e159]
|
||||
- text: Organization
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]:
|
||||
- generic [ref=e164]: Location Type *
|
||||
- combobox "Location Type *" [ref=e165]:
|
||||
- option "Select Location Type" [selected]
|
||||
- option "Outpatient"
|
||||
- option "Inpatient"
|
||||
- option "Emergency"
|
||||
- option "General"
|
||||
- generic [ref=e166]:
|
||||
- generic [ref=e167]: Area
|
||||
- combobox "Area" [ref=e168]:
|
||||
- option "Select Area (optional)" [selected]
|
||||
- option "Anesthesia (E2E-HOSP)"
|
||||
- option "Dialysis (E2E-HOSP)"
|
||||
- option "Emergency (E2E-HOSP)"
|
||||
- option "Endoscopy (E2E-HOSP)"
|
||||
- option "ICCU (E2E-HOSP)"
|
||||
- option "ICU (E2E-HOSP)"
|
||||
- option "ICU Stepdown (E2E-HOSP)"
|
||||
- option "LTACU (E2E-HOSP)"
|
||||
- option "Labor & Delivery (E2E-HOSP)"
|
||||
- option "Main OR (E2E-HOSP)"
|
||||
- option "Medical Ward (E2E-HOSP)"
|
||||
- option "NICU - Neonatal Intensive Care Unit (E2E-HOSP)"
|
||||
- option "Nursery (E2E-HOSP)"
|
||||
- option "OB Wards (E2E-HOSP)"
|
||||
- option "OPD1 (E2E-HOSP)"
|
||||
- option "OPD2 (E2E-HOSP)"
|
||||
- option "OPD3 (E2E-HOSP)"
|
||||
- option "OPD4 (E2E-HOSP)"
|
||||
- option "OPD5 (E2E-HOSP)"
|
||||
- option "OPD6 (E2E-HOSP)"
|
||||
- option "OPD7 (E2E-HOSP)"
|
||||
- option "PICU (E2E-HOSP)"
|
||||
- option "PICU Stepdown (E2E-HOSP)"
|
||||
- option "Pediatric Ward (E2E-HOSP)"
|
||||
- option "Recovery (E2E-HOSP)"
|
||||
- option "Stepdown 3-4 (E2E-HOSP)"
|
||||
- option "Surgical Wards (E2E-HOSP)"
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]: Department Category *
|
||||
- combobox [ref=e171]:
|
||||
- option "Select Category" [selected]
|
||||
- option "Medical"
|
||||
- option "Nursing"
|
||||
- option "Administrative"
|
||||
- option "Support Services"
|
||||
- generic [ref=e172]:
|
||||
- generic [ref=e173]: Department (Optional)
|
||||
- combobox "Department (Optional)" [ref=e174]:
|
||||
- option "Select Department" [selected]
|
||||
- option "Anesthesia Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Contact Center Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "Executive Administration"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Laundry Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Nursing Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Security Department"
|
||||
- option "Surgery Department"
|
||||
- generic [ref=e175]:
|
||||
- generic [ref=e176]: Section
|
||||
- combobox "Section" [ref=e177]:
|
||||
- option "Select Section" [selected]
|
||||
- generic [ref=e178]:
|
||||
- heading "Contact Information" [level=6] [ref=e179]:
|
||||
- img [ref=e180]
|
||||
- text: Contact Information
|
||||
- generic [ref=e183]:
|
||||
- generic [ref=e184]:
|
||||
- generic [ref=e185]: Contact Name *
|
||||
- textbox "Contact Name *" [ref=e186]
|
||||
- generic [ref=e187]:
|
||||
- generic [ref=e188]: Contact Phone *
|
||||
- textbox "Contact Phone *" [ref=e189]
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]: Contact Email
|
||||
- textbox "Contact Email" [ref=e192]
|
||||
- generic [ref=e193]:
|
||||
- heading "Inquiry Details" [level=6] [ref=e194]:
|
||||
- img [ref=e195]
|
||||
- text: Inquiry Details
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]: Inquiry Type *
|
||||
- combobox "Inquiry Type *" [ref=e201]:
|
||||
- option "General Inquiry" [selected]
|
||||
- option "Appointment Related"
|
||||
- option "Billing & Insurance"
|
||||
- option "Medical Records"
|
||||
- option "Pharmacy"
|
||||
- option "Insurance"
|
||||
- option "Feedback"
|
||||
- option "Other"
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Priority
|
||||
- combobox "Priority" [ref=e204]:
|
||||
- option "Low"
|
||||
- option "Medium" [selected]
|
||||
- option "High"
|
||||
- option "Critical"
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Subject *
|
||||
- textbox "Subject *" [ref=e207]:
|
||||
- /placeholder: Brief subject
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Message *
|
||||
- textbox "Message *" [ref=e210]:
|
||||
- /placeholder: Describe your inquiry
|
||||
- generic [ref=e211]:
|
||||
- button "Create Inquiry" [ref=e212] [cursor=pointer]:
|
||||
- img [ref=e213]
|
||||
- text: Create Inquiry
|
||||
- link "Cancel" [ref=e216] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- contentinfo [ref=e217]:
|
||||
- paragraph [ref=e218]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e219] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
124
.playwright-cli/page-2026-07-12T20-40-27-876Z.yml
Normal file
124
.playwright-cli/page-2026-07-12T20-40-27-876Z.yml
Normal file
@ -0,0 +1,124 @@
|
||||
- generic [active] [ref=e1]:
|
||||
- banner [ref=e2]:
|
||||
- generic [ref=e4]:
|
||||
- generic [ref=e5]:
|
||||
- img "HH Logo" [ref=e7]
|
||||
- generic [ref=e8]:
|
||||
- heading "PX360" [level=1] [ref=e9]
|
||||
- paragraph [ref=e10]: Patient Experience Management
|
||||
- generic [ref=e11]:
|
||||
- link "🇬🇧 English" [ref=e12] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e13]: 🇬🇧
|
||||
- generic [ref=e14]: English
|
||||
- link "🇸🇦 Arabic" [ref=e15] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e16]: 🇸🇦
|
||||
- generic [ref=e17]: Arabic
|
||||
- main [ref=e18]:
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- img [ref=e24]
|
||||
- heading "Report an Observation" [level=1] [ref=e27]
|
||||
- paragraph [ref=e28]: Help us improve by reporting issues you notice
|
||||
- generic [ref=e30]:
|
||||
- img [ref=e31]
|
||||
- generic [ref=e34]:
|
||||
- strong [ref=e35]: Anonymous Reporting
|
||||
- paragraph [ref=e36]: You can submit this report anonymously. Providing your information is optional but may help us follow up if needed.
|
||||
- generic [ref=e37]:
|
||||
- generic [ref=e38]:
|
||||
- generic [ref=e39]:
|
||||
- img [ref=e40]
|
||||
- text: Hospital
|
||||
- generic [ref=e44]: (optional)
|
||||
- combobox [ref=e45]:
|
||||
- option "Select hospital" [selected]
|
||||
- option "Al Nuzha"
|
||||
- option "Al Olya"
|
||||
- option "Al Suwaidi"
|
||||
- option "Al-Hammadi Hospital (KPI Test)"
|
||||
- option "E2E Test Hospital"
|
||||
- generic [ref=e46]:
|
||||
- generic [ref=e47]:
|
||||
- img [ref=e48]
|
||||
- text: Location Type
|
||||
- generic [ref=e51]: "*"
|
||||
- combobox [ref=e52]:
|
||||
- option "Select Location Type" [selected]
|
||||
- option "Outpatient (OP)"
|
||||
- option "Inpatient (IP)"
|
||||
- option "Emergency (ER)"
|
||||
- option "General"
|
||||
- generic [ref=e53]:
|
||||
- generic [ref=e54]:
|
||||
- img [ref=e55]
|
||||
- text: Category
|
||||
- generic [ref=e58]: (optional)
|
||||
- combobox [ref=e59]:
|
||||
- option "Select Category" [selected]
|
||||
- option "Medical"
|
||||
- option "Nursing"
|
||||
- option "Administrative"
|
||||
- option "Support Services"
|
||||
- generic [ref=e60]:
|
||||
- generic [ref=e61]:
|
||||
- img [ref=e62]
|
||||
- text: Description
|
||||
- generic [ref=e65]: "*"
|
||||
- textbox "Please describe what you observed in detail..." [ref=e66]
|
||||
- paragraph [ref=e67]: Please describe what you observed in detail.
|
||||
- generic [ref=e68]:
|
||||
- generic [ref=e69]:
|
||||
- img [ref=e70]
|
||||
- text: Location Details
|
||||
- generic [ref=e73]: (optional)
|
||||
- textbox "e.g., Building A, Floor 2, Room 205" [ref=e74]
|
||||
- generic [ref=e75]:
|
||||
- generic [ref=e76]:
|
||||
- img [ref=e77]
|
||||
- text: When did this occur?
|
||||
- textbox [ref=e79]: 2026-07-12T23:40:27
|
||||
- generic [ref=e80]:
|
||||
- generic [ref=e81]:
|
||||
- img [ref=e82]
|
||||
- text: Attachments
|
||||
- generic [ref=e84]: (optional)
|
||||
- generic [ref=e85] [cursor=pointer]:
|
||||
- img [ref=e86]
|
||||
- paragraph [ref=e89]: Click to upload files
|
||||
- paragraph [ref=e90]: Images, PDF, Word, Excel (max 10MB each)
|
||||
- generic [ref=e91]:
|
||||
- generic [ref=e92]:
|
||||
- img [ref=e93]
|
||||
- text: Your Information
|
||||
- generic [ref=e96]: (optional)
|
||||
- paragraph [ref=e97]: Providing your information helps us follow up if needed. Leave blank to submit anonymously.
|
||||
- generic [ref=e98]:
|
||||
- generic [ref=e99]:
|
||||
- generic [ref=e100]: Staff ID
|
||||
- textbox "Your staff ID (optional)" [ref=e101]
|
||||
- generic [ref=e102]:
|
||||
- generic [ref=e103]: Name
|
||||
- textbox "Your name (optional)" [ref=e104]
|
||||
- generic [ref=e105]:
|
||||
- generic [ref=e106]: Phone
|
||||
- textbox "Your phone number (optional)" [ref=e107]
|
||||
- generic [ref=e108]:
|
||||
- generic [ref=e109]: Email
|
||||
- textbox "Your email (optional)" [ref=e110]
|
||||
- button "Submit Observation" [ref=e111] [cursor=pointer]:
|
||||
- img [ref=e112]
|
||||
- text: Submit Observation
|
||||
- link "Track an existing observation" [ref=e116] [cursor=pointer]:
|
||||
- /url: /observations/track/
|
||||
- img [ref=e117]
|
||||
- text: Track an existing observation
|
||||
- contentinfo [ref=e120]:
|
||||
- generic [ref=e121]:
|
||||
- paragraph [ref=e122]: © 2026 Al Hammadi Hospital - PX360. All rights reserved.
|
||||
- paragraph [ref=e123]: Your feedback helps us improve our services
|
||||
- paragraph [ref=e124]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e125] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
133
.playwright-cli/page-2026-07-12T20-40-29-030Z.yml
Normal file
133
.playwright-cli/page-2026-07-12T20-40-29-030Z.yml
Normal file
@ -0,0 +1,133 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- link "Suggestions" [ref=e143] [cursor=pointer]:
|
||||
- /url: /suggestions/
|
||||
- img [ref=e144]
|
||||
- generic [ref=e146]: New
|
||||
- heading "New Suggestion" [level=1] [ref=e147]
|
||||
- link "Back" [ref=e148] [cursor=pointer]:
|
||||
- /url: /suggestions/
|
||||
- img [ref=e149]
|
||||
- text: Back
|
||||
- generic [ref=e151]:
|
||||
- generic [ref=e152]:
|
||||
- img [ref=e154]
|
||||
- heading "Suggestion Details" [level=5] [ref=e156]
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]:
|
||||
- generic [ref=e159]:
|
||||
- generic [ref=e160]: Your Name *
|
||||
- textbox "Your full name" [ref=e161]
|
||||
- generic [ref=e162]:
|
||||
- generic [ref=e163]: Phone Number *
|
||||
- textbox "Your phone number" [ref=e164]
|
||||
- generic [ref=e165]:
|
||||
- generic [ref=e166]: Hospital *
|
||||
- combobox [ref=e167]:
|
||||
- option "Select Hospital" [selected]
|
||||
- option "Al Nuzha"
|
||||
- option "Al Olya"
|
||||
- option "Al Suwaidi"
|
||||
- option "Al-Hammadi Hospital (KPI Test)"
|
||||
- option "E2E Test Hospital"
|
||||
- generic [ref=e168]:
|
||||
- generic [ref=e169]: Message *
|
||||
- textbox "Describe your suggestion in detail..." [ref=e170]
|
||||
- button "Submit Suggestion" [ref=e172] [cursor=pointer]:
|
||||
- img [ref=e173]
|
||||
- text: Submit Suggestion
|
||||
- contentinfo [ref=e176]:
|
||||
- paragraph [ref=e177]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e178] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
260
.playwright-cli/page-2026-07-12T20-40-30-328Z.yml
Normal file
260
.playwright-cli/page-2026-07-12T20-40-30-328Z.yml
Normal file
@ -0,0 +1,260 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e141]:
|
||||
- generic [ref=e142]:
|
||||
- heading "New Appreciation" [level=1] [ref=e143]:
|
||||
- img [ref=e144]
|
||||
- text: New Appreciation
|
||||
- paragraph [ref=e146]: Recognize a department for excellent service or care.
|
||||
- link "Back" [ref=e147] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e148]
|
||||
- text: Back
|
||||
- generic [ref=e150]:
|
||||
- generic [ref=e151]:
|
||||
- generic [ref=e152]: Hospital *
|
||||
- combobox "Hospital *" [ref=e153]:
|
||||
- option "---------"
|
||||
- option "Al Nuzha"
|
||||
- option "Al Olya"
|
||||
- option "Al Suwaidi"
|
||||
- option "Al-Hammadi Hospital (KPI Test)"
|
||||
- option "E2E Test Hospital" [selected]
|
||||
- generic [ref=e154]:
|
||||
- generic [ref=e155]: Department *
|
||||
- combobox "Department *" [ref=e156]:
|
||||
- option "---------" [selected]
|
||||
- option "Anesthesia Department — Al Nuzha"
|
||||
- option "Biomedical Department — Al Nuzha"
|
||||
- option "Contact Center Department — Al Nuzha"
|
||||
- option "Critical Care Department — Al Nuzha"
|
||||
- option "Dental Department — Al Nuzha"
|
||||
- option "Dermatology Department — Al Nuzha"
|
||||
- option "Emergency Administrative Department — Al Nuzha"
|
||||
- option "Emergency Medicine Department — Al Nuzha"
|
||||
- option "Executive Administration — Al Nuzha"
|
||||
- option "Facility Management & Maintenance Department — Al Nuzha"
|
||||
- option "Financial Collection & Claims Department — Al Nuzha"
|
||||
- option "Food Services Department — Al Nuzha"
|
||||
- option "Housekeeping & Hospitality Department — Al Nuzha"
|
||||
- option "IVF — Al Nuzha"
|
||||
- option "Infection Control Department — Al Nuzha"
|
||||
- option "Information Technology Department — Al Nuzha"
|
||||
- option "Inpatient Department — Al Nuzha"
|
||||
- option "Internal Medicine Department — Al Nuzha"
|
||||
- option "Laboratory Department — Al Nuzha"
|
||||
- option "Laundry Department — Al Nuzha"
|
||||
- option "Medical Administration — Al Nuzha"
|
||||
- option "Medical Ancillary Services Department — Al Nuzha"
|
||||
- option "Medical Approvals Department — Al Nuzha"
|
||||
- option "Medical Records Department — Al Nuzha"
|
||||
- option "Nursing Department — Al Nuzha"
|
||||
- option "ONCOLOGY Department — Al Nuzha"
|
||||
- option "Obstetrics & Gynecology Department — Al Nuzha"
|
||||
- option "Operating Rooms (OR) Department — Al Nuzha"
|
||||
- option "Ophthalmology Department — Al Nuzha"
|
||||
- option "Outpatient Department — Al Nuzha"
|
||||
- option "Patient Affairs Department — Al Nuzha"
|
||||
- option "Patient Experience Department — Al Nuzha"
|
||||
- option "Pediatric Department — Al Nuzha"
|
||||
- option "Pharmacy Department — Al Nuzha"
|
||||
- option "Radiology Department — Al Nuzha"
|
||||
- option "Security Department — Al Nuzha"
|
||||
- option "Surgery Department — Al Nuzha"
|
||||
- option "Facility Management & Maintenance Department — Al Olya"
|
||||
- option "HR Department — Al Olya"
|
||||
- option "Support Services Department — Al Olya"
|
||||
- option "Anesthesia Department — Al Suwaidi"
|
||||
- option "Biomedical Department — Al Suwaidi"
|
||||
- option "Contact Center Department — Al Suwaidi"
|
||||
- option "Critical Care Department — Al Suwaidi"
|
||||
- option "Dental Department — Al Suwaidi"
|
||||
- option "Dermatology Department — Al Suwaidi"
|
||||
- option "Emergency Administrative Department — Al Suwaidi"
|
||||
- option "Emergency Medicine Department — Al Suwaidi"
|
||||
- option "Executive Administration — Al Suwaidi"
|
||||
- option "Facility Management & Maintenance Department — Al Suwaidi"
|
||||
- option "Financial Collection & Claims Department — Al Suwaidi"
|
||||
- option "Food Services Department — Al Suwaidi"
|
||||
- option "Housekeeping & Hospitality Department — Al Suwaidi"
|
||||
- option "IVF — Al Suwaidi"
|
||||
- option "Infection Control Department — Al Suwaidi"
|
||||
- option "Information Technology Department — Al Suwaidi"
|
||||
- option "Inpatient Department — Al Suwaidi"
|
||||
- option "Internal Medicine Department — Al Suwaidi"
|
||||
- option "Laboratory Department — Al Suwaidi"
|
||||
- option "Laundry Department — Al Suwaidi"
|
||||
- option "Medical Administration — Al Suwaidi"
|
||||
- option "Medical Ancillary Services Department — Al Suwaidi"
|
||||
- option "Medical Approvals Department — Al Suwaidi"
|
||||
- option "Medical Records Department — Al Suwaidi"
|
||||
- option "Nursing Department — Al Suwaidi"
|
||||
- option "ONCOLOGY Department — Al Suwaidi"
|
||||
- option "Obstetrics & Gynecology Department — Al Suwaidi"
|
||||
- option "Operating Rooms (OR) Department — Al Suwaidi"
|
||||
- option "Ophthalmology Department — Al Suwaidi"
|
||||
- option "Outpatient Department — Al Suwaidi"
|
||||
- option "Patient Affairs Department — Al Suwaidi"
|
||||
- option "Patient Experience Department — Al Suwaidi"
|
||||
- option "Pediatric Department — Al Suwaidi"
|
||||
- option "Pharmacy Department — Al Suwaidi"
|
||||
- option "Radiology Department — Al Suwaidi"
|
||||
- option "Security Department — Al Suwaidi"
|
||||
- option "Surgery Department — Al Suwaidi"
|
||||
- option "Administration — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Cardiology — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Emergency — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Housekeeping — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "ICU — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Laboratory — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Maintenance — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Medical Reports — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Nursing — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Orthopedics — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Pediatrics — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Pharmacy — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Reception — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Security — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Surgery — Al-Hammadi Hospital (KPI Test)"
|
||||
- option "Anesthesia Department — E2E Test Hospital"
|
||||
- option "Biomedical Department — E2E Test Hospital"
|
||||
- option "Contact Center Department — E2E Test Hospital"
|
||||
- option "Critical Care Department — E2E Test Hospital"
|
||||
- option "Dental Department — E2E Test Hospital"
|
||||
- option "Dermatology Department — E2E Test Hospital"
|
||||
- option "E2E Dept 1781720032405 — E2E Test Hospital"
|
||||
- option "E2E Dept 1781720188555 — E2E Test Hospital"
|
||||
- option "Emergency Administrative Department — E2E Test Hospital"
|
||||
- option "Emergency Medicine Department — E2E Test Hospital"
|
||||
- option "Executive Administration — E2E Test Hospital"
|
||||
- option "Facility Management & Maintenance Department — E2E Test Hospital"
|
||||
- option "Financial Collection & Claims Department — E2E Test Hospital"
|
||||
- option "Food Services Department — E2E Test Hospital"
|
||||
- option "Housekeeping & Hospitality Department — E2E Test Hospital"
|
||||
- option "IVF — E2E Test Hospital"
|
||||
- option "Infection Control Department — E2E Test Hospital"
|
||||
- option "Information Technology Department — E2E Test Hospital"
|
||||
- option "Inpatient Department — E2E Test Hospital"
|
||||
- option "Internal Medicine Department — E2E Test Hospital"
|
||||
- option "Laboratory Department — E2E Test Hospital"
|
||||
- option "Laundry Department — E2E Test Hospital"
|
||||
- option "Medical Administration — E2E Test Hospital"
|
||||
- option "Medical Ancillary Services Department — E2E Test Hospital"
|
||||
- option "Medical Approvals Department — E2E Test Hospital"
|
||||
- option "Medical Records Department — E2E Test Hospital"
|
||||
- option "Nursing Department — E2E Test Hospital"
|
||||
- option "ONCOLOGY Department — E2E Test Hospital"
|
||||
- option "Obstetrics & Gynecology Department — E2E Test Hospital"
|
||||
- option "Operating Rooms (OR) Department — E2E Test Hospital"
|
||||
- option "Ophthalmology Department — E2E Test Hospital"
|
||||
- option "Outpatient Department — E2E Test Hospital"
|
||||
- option "Patient Affairs Department — E2E Test Hospital"
|
||||
- option "Patient Experience Department — E2E Test Hospital"
|
||||
- option "Pediatric Department — E2E Test Hospital"
|
||||
- option "Pharmacy Department — E2E Test Hospital"
|
||||
- option "Radiology Department — E2E Test Hospital"
|
||||
- option "Security Department — E2E Test Hospital"
|
||||
- option "Surgery Department — E2E Test Hospital"
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]: Message *
|
||||
- textbox "Message *" [ref=e159]:
|
||||
- /placeholder: Write your message of appreciation...
|
||||
- generic [ref=e160]:
|
||||
- button "Create Appreciation" [ref=e161] [cursor=pointer]:
|
||||
- img [ref=e162]
|
||||
- text: Create Appreciation
|
||||
- link "Cancel" [ref=e164] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- contentinfo [ref=e165]:
|
||||
- paragraph [ref=e166]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e167] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
172
.playwright-cli/page-2026-07-12T20-40-51-825Z.yml
Normal file
172
.playwright-cli/page-2026-07-12T20-40-51-825Z.yml
Normal file
@ -0,0 +1,172 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Inquiries" [ref=e141] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: INQ-202607-0007
|
||||
- generic [ref=e145]: Open
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Not Sent to Dept
|
||||
- generic [ref=e150]:
|
||||
- heading "test12121212" [level=1] [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- img [ref=e154]
|
||||
- text: 2026-07-12
|
||||
- generic [ref=e157]:
|
||||
- generic [ref=e158]:
|
||||
- img [ref=e160]
|
||||
- generic [ref=e162]: Created
|
||||
- generic [ref=e164]:
|
||||
- img [ref=e166]
|
||||
- generic [ref=e169]: Assigned
|
||||
- generic [ref=e171]:
|
||||
- img [ref=e173]
|
||||
- generic [ref=e176]: Sent to Dept
|
||||
- generic [ref=e178]:
|
||||
- img [ref=e180]
|
||||
- generic [ref=e182]: Dept Response
|
||||
- generic [ref=e184]:
|
||||
- img [ref=e186]
|
||||
- generic [ref=e189]: Resolved
|
||||
- generic [ref=e190]:
|
||||
- generic [ref=e191]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e195]:
|
||||
- paragraph [ref=e196]: Activate this inquiry
|
||||
- paragraph [ref=e197]: Activation moves it to In Progress so actions become available.
|
||||
- generic [ref=e198]:
|
||||
- button "Cancel" [ref=e200] [cursor=pointer]
|
||||
- button "Activate" [ref=e202] [cursor=pointer]
|
||||
- navigation [ref=e203]:
|
||||
- button "Details" [ref=e204] [cursor=pointer]
|
||||
- button "Timeline" [ref=e205] [cursor=pointer]
|
||||
- button "Notes" [ref=e206] [cursor=pointer]
|
||||
- main [ref=e207]:
|
||||
- generic [ref=e209]:
|
||||
- generic [ref=e210]:
|
||||
- generic [ref=e211]:
|
||||
- paragraph
|
||||
- paragraph [ref=e212]: "231313131"
|
||||
- paragraph
|
||||
- generic [ref=e213]:
|
||||
- generic [ref=e215]:
|
||||
- img [ref=e216]
|
||||
- generic [ref=e219]: AI Analysis
|
||||
- generic [ref=e220]:
|
||||
- paragraph [ref=e221]: "231313131"
|
||||
- paragraph [ref=e222]: "231313131"
|
||||
- generic [ref=e223]:
|
||||
- heading "Patient" [level=3] [ref=e224]:
|
||||
- img [ref=e225]
|
||||
- text: Patient
|
||||
- generic [ref=e228]:
|
||||
- generic [ref=e229]: ismail mosa
|
||||
- generic [ref=e230]: "|"
|
||||
- generic [ref=e231]:
|
||||
- img [ref=e232]
|
||||
- text: "0569874563"
|
||||
- generic [ref=e235]:
|
||||
- heading "Quick Actions" [level=3] [ref=e236]
|
||||
- generic [ref=e237]:
|
||||
- button "Activate" [ref=e239] [cursor=pointer]:
|
||||
- img [ref=e240]
|
||||
- generic [ref=e243]: Activate
|
||||
- button "Cancel Inquiry" [ref=e245] [cursor=pointer]:
|
||||
- img [ref=e246]
|
||||
- generic [ref=e250]: Cancel Inquiry
|
||||
- contentinfo [ref=e251]:
|
||||
- paragraph [ref=e252]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e253] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
243
.playwright-cli/page-2026-07-12T20-40-53-136Z.yml
Normal file
243
.playwright-cli/page-2026-07-12T20-40-53-136Z.yml
Normal file
@ -0,0 +1,243 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e140]:
|
||||
- generic [ref=e141]:
|
||||
- heading "Complaints Registry" [level=1] [ref=e142]
|
||||
- paragraph [ref=e143]: Manage and monitor patient feedback in real-time
|
||||
- generic [ref=e144]:
|
||||
- generic [ref=e145]:
|
||||
- img [ref=e146]
|
||||
- textbox "Search ID, Name or Dept..." [ref=e149]
|
||||
- link "Public Form" [ref=e150] [cursor=pointer]:
|
||||
- /url: /core/public/submit/
|
||||
- img [ref=e151]
|
||||
- text: Public Form
|
||||
- button "Export" [ref=e155] [cursor=pointer]:
|
||||
- img [ref=e156]
|
||||
- text: Export
|
||||
- link "New Case" [ref=e159] [cursor=pointer]:
|
||||
- /url: /complaints/new/
|
||||
- img [ref=e160]
|
||||
- text: New Case
|
||||
- generic [ref=e161]:
|
||||
- generic [ref=e162]:
|
||||
- img [ref=e165]
|
||||
- generic [ref=e169]:
|
||||
- heading "Total Received" [level=6] [ref=e170]
|
||||
- heading "3" [level=2] [ref=e171]
|
||||
- generic [ref=e172]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e178]:
|
||||
- heading "Resolved" [level=6] [ref=e179]
|
||||
- heading "0 (0.0%)" [level=2] [ref=e180]
|
||||
- generic [ref=e181]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e187]:
|
||||
- heading "Pending" [level=6] [ref=e188]
|
||||
- heading "3 (100.0%)" [level=2] [ref=e189]
|
||||
- generic [ref=e190]:
|
||||
- img [ref=e193]
|
||||
- generic [ref=e195]:
|
||||
- heading "TAT Alert" [level=6] [ref=e196]
|
||||
- heading "3 (100.0%)" [level=2] [ref=e197]
|
||||
- generic [ref=e198]:
|
||||
- generic [ref=e199]:
|
||||
- generic [ref=e200]:
|
||||
- img [ref=e202]
|
||||
- heading "Complaints" [level=5] [ref=e204]
|
||||
- generic [ref=e205]:
|
||||
- link "All Cases" [ref=e206] [cursor=pointer]:
|
||||
- /url: "?"
|
||||
- link "Pending" [ref=e207] [cursor=pointer]:
|
||||
- /url: "?status=open"
|
||||
- link "In Progress" [ref=e208] [cursor=pointer]:
|
||||
- /url: "?status=in_progress"
|
||||
- link "Resolved" [ref=e209] [cursor=pointer]:
|
||||
- /url: "?status=resolved"
|
||||
- button "Advanced" [ref=e211] [cursor=pointer]:
|
||||
- img [ref=e212]
|
||||
- text: Advanced
|
||||
- paragraph [ref=e213]: "Showing: 1-3 of 3"
|
||||
- generic [ref=e214]:
|
||||
- table [ref=e215]:
|
||||
- rowgroup [ref=e216]:
|
||||
- row "Complaint ID Patient Name Description Source Department Status SLA Severity Priority Assigned Actions" [ref=e217]:
|
||||
- columnheader "Complaint ID" [ref=e218]
|
||||
- columnheader "Patient Name" [ref=e219]
|
||||
- columnheader "Description" [ref=e220]
|
||||
- columnheader "Source" [ref=e221]
|
||||
- columnheader "Department" [ref=e222]
|
||||
- columnheader "Status" [ref=e223]
|
||||
- columnheader "SLA" [ref=e224]
|
||||
- columnheader "Severity" [ref=e225]
|
||||
- columnheader "Priority" [ref=e226]
|
||||
- columnheader "Assigned" [ref=e227]
|
||||
- columnheader "Actions" [ref=e228]
|
||||
- rowgroup [ref=e229]:
|
||||
- row "CMP-202607-0006 test شكوى عامة Patient Patient Experience Department In Progress Overdue 21h 47m Medium Medium test test" [ref=e230] [cursor=pointer]:
|
||||
- cell "CMP-202607-0006" [ref=e231]
|
||||
- cell "test" [ref=e232]:
|
||||
- generic [ref=e233]: test
|
||||
- cell "شكوى عامة" [ref=e234]:
|
||||
- generic [ref=e235]: شكوى عامة
|
||||
- button "View description" [ref=e236]:
|
||||
- img [ref=e237]
|
||||
- cell "Patient" [ref=e246]
|
||||
- cell "Patient Experience Department" [ref=e247]
|
||||
- cell "In Progress" [ref=e248]
|
||||
- cell "Overdue 21h 47m" [ref=e249]
|
||||
- cell "Medium" [ref=e250]
|
||||
- cell "Medium" [ref=e251]
|
||||
- cell "test test" [ref=e252]
|
||||
- cell [ref=e253]:
|
||||
- generic [ref=e254]:
|
||||
- link "View" [ref=e255]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/
|
||||
- img [ref=e256]
|
||||
- link "Assign" [ref=e259]:
|
||||
- /url: /complaints/f8aa6216-bb94-455e-ba18-d16a01024662/assign/
|
||||
- img [ref=e260]
|
||||
- row "CMP-202607-0005 ismail شكوى اختبار Patient Contact Center Department In Progress Overdue 23h 22m Low Low test test" [ref=e263] [cursor=pointer]:
|
||||
- cell "CMP-202607-0005" [ref=e264]
|
||||
- cell "ismail" [ref=e265]:
|
||||
- generic [ref=e266]: ismail
|
||||
- cell "شكوى اختبار" [ref=e267]:
|
||||
- generic [ref=e268]: شكوى اختبار
|
||||
- button "View description" [ref=e269]:
|
||||
- img [ref=e270]
|
||||
- cell "Patient" [ref=e279]
|
||||
- cell "Contact Center Department" [ref=e280]
|
||||
- cell "In Progress" [ref=e281]
|
||||
- cell "Overdue 23h 22m" [ref=e282]
|
||||
- cell "Low" [ref=e283]
|
||||
- cell "Low" [ref=e284]
|
||||
- cell "test test" [ref=e285]
|
||||
- cell [ref=e286]:
|
||||
- link "View" [ref=e288]:
|
||||
- /url: /complaints/9ca01d52-712d-4bca-b30d-6f1f818ff839/
|
||||
- img [ref=e289]
|
||||
- row "CMP-202607-0004 OVR ismail اختبار عام Internal Contact Center Department In Progress Overdue 4d 7h 22m Low Low ismail mosa" [ref=e292] [cursor=pointer]:
|
||||
- cell "CMP-202607-0004 OVR" [ref=e293]
|
||||
- cell "ismail" [ref=e294]:
|
||||
- generic [ref=e295]: ismail
|
||||
- cell "اختبار عام" [ref=e296]:
|
||||
- generic [ref=e297]: اختبار عام
|
||||
- button "View description" [ref=e298]:
|
||||
- img [ref=e299]
|
||||
- cell "Internal" [ref=e308]
|
||||
- cell "Contact Center Department" [ref=e309]
|
||||
- cell "In Progress" [ref=e310]
|
||||
- cell "Overdue 4d 7h 22m" [ref=e311]
|
||||
- cell "Low" [ref=e312]
|
||||
- cell "Low" [ref=e313]
|
||||
- cell "ismail mosa" [ref=e314]
|
||||
- cell [ref=e315]:
|
||||
- link "View" [ref=e317]:
|
||||
- /url: /complaints/1c4e8dde-48d8-42ab-8c8d-4ada8314fc40/
|
||||
- img [ref=e318]
|
||||
- generic [ref=e321]:
|
||||
- generic [ref=e322]:
|
||||
- generic [ref=e323]: Showing 1-3 of 3
|
||||
- generic [ref=e324]:
|
||||
- generic [ref=e325]: Show
|
||||
- combobox [ref=e326]:
|
||||
- option "10"
|
||||
- option "25" [selected]
|
||||
- option "50"
|
||||
- option "100"
|
||||
- generic [ref=e327]:
|
||||
- img [ref=e329]
|
||||
- generic [ref=e331]: "1"
|
||||
- img [ref=e333]
|
||||
- contentinfo [ref=e335]:
|
||||
- paragraph [ref=e336]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e337] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
259
.playwright-cli/page-2026-07-12T20-40-56-706Z.yml
Normal file
259
.playwright-cli/page-2026-07-12T20-40-56-706Z.yml
Normal file
@ -0,0 +1,259 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Observations" [ref=e141] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: OBS-202607-0001
|
||||
- generic [ref=e145]: In Progress
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Not Sent to Dept
|
||||
- heading "test observation" [level=1] [ref=e151]
|
||||
- generic [ref=e153]:
|
||||
- generic [ref=e154]:
|
||||
- img [ref=e156]
|
||||
- generic [ref=e158]: Created
|
||||
- generic [ref=e160]:
|
||||
- img [ref=e162]
|
||||
- generic [ref=e164]: Assigned
|
||||
- generic [ref=e166]:
|
||||
- img [ref=e168]
|
||||
- generic [ref=e171]: Sent to Dept
|
||||
- generic [ref=e173]:
|
||||
- img [ref=e175]
|
||||
- generic [ref=e177]: Dept Response
|
||||
- generic [ref=e179]:
|
||||
- img [ref=e181]
|
||||
- generic [ref=e184]: Resolved
|
||||
- navigation [ref=e185]:
|
||||
- button "Details" [ref=e186] [cursor=pointer]
|
||||
- button "Timeline" [ref=e187] [cursor=pointer]
|
||||
- button "RCA" [ref=e188] [cursor=pointer]
|
||||
- button "Notes" [ref=e189] [cursor=pointer]
|
||||
- main [ref=e190]:
|
||||
- generic [ref=e192]:
|
||||
- generic [ref=e193]:
|
||||
- paragraph [ref=e195]: test observation
|
||||
- generic [ref=e196]:
|
||||
- generic [ref=e197]:
|
||||
- paragraph [ref=e198]: Category
|
||||
- paragraph [ref=e199]: "-"
|
||||
- generic [ref=e200]:
|
||||
- paragraph [ref=e201]: Location
|
||||
- paragraph [ref=e202]: "-"
|
||||
- generic [ref=e203]:
|
||||
- paragraph [ref=e204]: Incident Date
|
||||
- paragraph [ref=e205]: 08 Jul 2026
|
||||
- generic [ref=e209]:
|
||||
- img [ref=e210]
|
||||
- paragraph [ref=e214]: No department assigned to this observation
|
||||
- generic [ref=e215]:
|
||||
- generic [ref=e216]:
|
||||
- heading "Quick Actions" [level=3] [ref=e217]
|
||||
- generic [ref=e218]:
|
||||
- button "Respond to Patient" [ref=e219] [cursor=pointer]:
|
||||
- img [ref=e220]
|
||||
- generic [ref=e222]: Respond to Patient
|
||||
- button "Reassign" [ref=e223] [cursor=pointer]:
|
||||
- img [ref=e224]
|
||||
- generic [ref=e227]: Reassign
|
||||
- button "Send to Dept" [ref=e228] [cursor=pointer]:
|
||||
- img [ref=e229]
|
||||
- generic [ref=e232]: Send to Dept
|
||||
- button "Resolve" [ref=e234] [cursor=pointer]:
|
||||
- img [ref=e235]
|
||||
- generic [ref=e238]: Resolve
|
||||
- link "RCA" [ref=e239] [cursor=pointer]:
|
||||
- /url: /rca/create/?related_model=observation&related_id=1d925706-110e-4bad-808f-26aae2029144
|
||||
- img [ref=e240]
|
||||
- generic [ref=e243]: RCA
|
||||
- link "QI Project" [ref=e244] [cursor=pointer]:
|
||||
- /url: /projects/create/?related_model=observation&related_id=1d925706-110e-4bad-808f-26aae2029144
|
||||
- img [ref=e245]
|
||||
- generic [ref=e247]: QI Project
|
||||
- button "Delete" [ref=e249] [cursor=pointer]:
|
||||
- img [ref=e250]
|
||||
- generic [ref=e253]: Delete
|
||||
- generic [ref=e254]:
|
||||
- heading "Assignment" [level=3] [ref=e255]:
|
||||
- img [ref=e256]
|
||||
- text: Assignment
|
||||
- list [ref=e261]:
|
||||
- listitem [ref=e262]:
|
||||
- generic [ref=e263]: Department
|
||||
- generic [ref=e264]: Contact Center Department
|
||||
- listitem [ref=e265]:
|
||||
- generic [ref=e266]: Assigned To
|
||||
- generic [ref=e267]: ismail mosa
|
||||
- generic [ref=e268]:
|
||||
- heading "Linked PX Action" [level=3] [ref=e269]:
|
||||
- img [ref=e270]
|
||||
- text: Linked PX Action
|
||||
- paragraph [ref=e273]: Observation OBS-202607-0001
|
||||
- link "View Action" [ref=e274] [cursor=pointer]:
|
||||
- /url: /actions/9b185c2a-e3fe-4e13-a727-7aab21df8754/
|
||||
- img [ref=e275]
|
||||
- text: View Action
|
||||
- generic [ref=e277]:
|
||||
- heading "Triage" [level=3] [ref=e278]:
|
||||
- img [ref=e279]
|
||||
- text: Triage
|
||||
- generic [ref=e280]:
|
||||
- generic [ref=e281]:
|
||||
- generic [ref=e282]: Department
|
||||
- combobox [ref=e283]:
|
||||
- option "Select department"
|
||||
- option "Contact Center Department" [selected]
|
||||
- option "Emergency Administrative Department"
|
||||
- option "Executive Administration"
|
||||
- option "Financial Collection & Claims Department"
|
||||
- option "Inpatient Department"
|
||||
- option "Medical Approvals Department"
|
||||
- option "Medical Records Department"
|
||||
- option "Outpatient Department"
|
||||
- option "Patient Affairs Department"
|
||||
- option "Patient Experience Department"
|
||||
- option "Security Department"
|
||||
- option "Anesthesia Department"
|
||||
- option "Critical Care Department"
|
||||
- option "Dental Department"
|
||||
- option "Dermatology Department"
|
||||
- option "Emergency Medicine Department"
|
||||
- option "IVF"
|
||||
- option "Infection Control Department"
|
||||
- option "Internal Medicine Department"
|
||||
- option "Laboratory Department"
|
||||
- option "Medical Administration"
|
||||
- option "Medical Ancillary Services Department"
|
||||
- option "ONCOLOGY Department"
|
||||
- option "Obstetrics & Gynecology Department"
|
||||
- option "Operating Rooms (OR) Department"
|
||||
- option "Ophthalmology Department"
|
||||
- option "Pediatric Department"
|
||||
- option "Pharmacy Department"
|
||||
- option "Radiology Department"
|
||||
- option "Surgery Department"
|
||||
- option "E2E Dept 1781720032405"
|
||||
- option "E2E Dept 1781720188555"
|
||||
- option "Nursing Department"
|
||||
- option "Biomedical Department"
|
||||
- option "Facility Management & Maintenance Department"
|
||||
- option "Food Services Department"
|
||||
- option "Housekeeping & Hospitality Department"
|
||||
- option "Information Technology Department"
|
||||
- option "Laundry Department"
|
||||
- generic [ref=e284]:
|
||||
- generic [ref=e285]: Assign To
|
||||
- combobox [ref=e286]:
|
||||
- option "Select assignee" [selected]
|
||||
- option "E2E PX Employee (e2e-px-employee@px360.test)"
|
||||
- option "test test (9m5s6gzmft@ruutukf.com)"
|
||||
- option "test test (esma3el555@gmail.com)"
|
||||
- generic [ref=e287]:
|
||||
- generic [ref=e288]: Status
|
||||
- combobox [ref=e289]:
|
||||
- option "Open"
|
||||
- option "In Progress" [selected]
|
||||
- option "Resolved"
|
||||
- option "Closed"
|
||||
- generic [ref=e290]:
|
||||
- generic [ref=e291]: Note
|
||||
- textbox "Add a note about this triage action (optional)..." [ref=e292]
|
||||
- button "Update" [ref=e293] [cursor=pointer]:
|
||||
- img [ref=e294]
|
||||
- text: Update
|
||||
- contentinfo [ref=e297]:
|
||||
- paragraph [ref=e298]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e299] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
188
.playwright-cli/page-2026-07-12T20-40-58-505Z.yml
Normal file
188
.playwright-cli/page-2026-07-12T20-40-58-505Z.yml
Normal file
@ -0,0 +1,188 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Suggestions" [ref=e141] [cursor=pointer]:
|
||||
- /url: /suggestions/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: test
|
||||
- generic [ref=e145]: Submitted
|
||||
- generic [ref=e146]: Medium
|
||||
- generic [ref=e147]:
|
||||
- heading "Suggestion Detail" [level=1] [ref=e148]
|
||||
- link "Edit" [ref=e150] [cursor=pointer]:
|
||||
- /url: /suggestions/04b009f3-53aa-4eac-9baf-39b3633e56b0/update/
|
||||
- img [ref=e151]
|
||||
- text: Edit
|
||||
- generic [ref=e155]:
|
||||
- generic [ref=e156]:
|
||||
- img [ref=e158]
|
||||
- generic [ref=e160]: Submitted
|
||||
- generic [ref=e162]:
|
||||
- img [ref=e164]
|
||||
- generic [ref=e167]: Assigned
|
||||
- generic [ref=e169]:
|
||||
- img [ref=e171]
|
||||
- generic [ref=e174]: Reviewed
|
||||
- generic [ref=e176]:
|
||||
- img [ref=e178]
|
||||
- generic [ref=e180]: Acknowledged
|
||||
- generic [ref=e182]:
|
||||
- img [ref=e184]
|
||||
- generic [ref=e187]: Closed
|
||||
- navigation [ref=e188]:
|
||||
- button "Details" [ref=e189] [cursor=pointer]
|
||||
- button "AI Analysis" [ref=e190] [cursor=pointer]
|
||||
- button "Notes" [ref=e191] [cursor=pointer]
|
||||
- main [ref=e192]:
|
||||
- generic [ref=e195]:
|
||||
- heading "Suggestion Details" [level=3] [ref=e196]:
|
||||
- img [ref=e197]
|
||||
- text: Suggestion Details
|
||||
- paragraph [ref=e200]: test
|
||||
- generic [ref=e201]:
|
||||
- generic [ref=e202]:
|
||||
- generic [ref=e203]: Hospital
|
||||
- generic [ref=e204]: E2E Test Hospital
|
||||
- generic [ref=e205]:
|
||||
- generic [ref=e206]: Category
|
||||
- generic [ref=e207]: Other
|
||||
- generic [ref=e208]:
|
||||
- generic [ref=e209]: Location
|
||||
- generic [ref=e210]: Not specified
|
||||
- generic [ref=e211]:
|
||||
- generic [ref=e212]: Sentiment
|
||||
- generic [ref=e214]: Neutral
|
||||
- generic [ref=e215]:
|
||||
- heading "Contact" [level=6] [ref=e216]
|
||||
- generic [ref=e217]:
|
||||
- generic [ref=e218]: ismail
|
||||
- generic [ref=e219]: "0569874589"
|
||||
- generic [ref=e221]:
|
||||
- img [ref=e222]
|
||||
- text: July 09, 2026 13:46
|
||||
- generic [ref=e225]:
|
||||
- heading "Quick Actions" [level=3] [ref=e226]
|
||||
- generic [ref=e227]:
|
||||
- generic [ref=e228]:
|
||||
- text: Status
|
||||
- combobox [ref=e229]:
|
||||
- option "Submitted" [selected]
|
||||
- option "Reviewed"
|
||||
- option "Acknowledged"
|
||||
- option "Closed"
|
||||
- option "Reopened"
|
||||
- textbox "Add a note..." [ref=e230]
|
||||
- button "Update" [ref=e231] [cursor=pointer]:
|
||||
- img [ref=e232]
|
||||
- text: Update
|
||||
- separator [ref=e237]
|
||||
- generic [ref=e238]:
|
||||
- text: Assign To
|
||||
- combobox [ref=e239]:
|
||||
- option "Select user..." [selected]
|
||||
- option "E2E PX Employee"
|
||||
- option "test test"
|
||||
- option "test test"
|
||||
- button "Assign" [ref=e240] [cursor=pointer]:
|
||||
- img [ref=e241]
|
||||
- text: Assign
|
||||
- separator [ref=e245]
|
||||
- generic [ref=e246]:
|
||||
- text: Department
|
||||
- button "Send to Department" [ref=e247] [cursor=pointer]:
|
||||
- img [ref=e248]
|
||||
- text: Send to Department
|
||||
- separator [ref=e251]
|
||||
- contentinfo [ref=e252]:
|
||||
- paragraph [ref=e253]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e254] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
137
.playwright-cli/page-2026-07-12T20-41-00-163Z.yml
Normal file
137
.playwright-cli/page-2026-07-12T20-41-00-163Z.yml
Normal file
@ -0,0 +1,137 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img "Al Hammadi" [ref=e6]
|
||||
- img "Al Hammadi" [ref=e8]
|
||||
- navigation [ref=e9]:
|
||||
- link [ref=e10] [cursor=pointer]:
|
||||
- /url: /analytics/dashboard/
|
||||
- img [ref=e11]
|
||||
- link [ref=e16] [cursor=pointer]:
|
||||
- /url: /projects/my-tasks/
|
||||
- img [ref=e17]
|
||||
- link [ref=e20] [cursor=pointer]:
|
||||
- /url: /
|
||||
- img [ref=e21]
|
||||
- link [ref=e23] [cursor=pointer]:
|
||||
- /url: /complaints/
|
||||
- img [ref=e24]
|
||||
- link [ref=e27] [cursor=pointer]:
|
||||
- /url: /inquiries/
|
||||
- img [ref=e28]
|
||||
- link [ref=e31] [cursor=pointer]:
|
||||
- /url: /observations/
|
||||
- img [ref=e32]
|
||||
- link [ref=e35] [cursor=pointer]:
|
||||
- /url: /suggestions/?feedback_type=suggestion
|
||||
- img [ref=e36]
|
||||
- link [ref=e38] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e39]
|
||||
- link [ref=e41] [cursor=pointer]:
|
||||
- /url: /organizations/patients/
|
||||
- img [ref=e42]
|
||||
- button [ref=e46] [cursor=pointer]:
|
||||
- img [ref=e47]
|
||||
- button [ref=e52] [cursor=pointer]:
|
||||
- img [ref=e53]
|
||||
- link [ref=e58] [cursor=pointer]:
|
||||
- /url: /organizations/departments/
|
||||
- img [ref=e59]
|
||||
- link [ref=e63] [cursor=pointer]:
|
||||
- /url: /projects/
|
||||
- img [ref=e64]
|
||||
- button [ref=e69] [cursor=pointer]:
|
||||
- img [ref=e70]
|
||||
- button [ref=e74] [cursor=pointer]:
|
||||
- img [ref=e75]
|
||||
- button [ref=e80] [cursor=pointer]:
|
||||
- img [ref=e81]
|
||||
- button [ref=e88] [cursor=pointer]:
|
||||
- img [ref=e89]
|
||||
- button [ref=e93] [cursor=pointer]:
|
||||
- img [ref=e94]
|
||||
- link [ref=e96] [cursor=pointer]:
|
||||
- /url: /accounts/acknowledgements/my-acknowledgements/
|
||||
- img [ref=e97]
|
||||
- button [ref=e103] [cursor=pointer]:
|
||||
- img [ref=e104]
|
||||
- generic [ref=e107]:
|
||||
- link "🇬🇧" [ref=e108] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e109]: 🇬🇧
|
||||
- link "🇸🇦" [ref=e110] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e111]: 🇸🇦
|
||||
- generic [ref=e112]:
|
||||
- banner [ref=e113]:
|
||||
- generic [ref=e114]:
|
||||
- heading "Welcome, E2E" [level=2] [ref=e115]
|
||||
- paragraph [ref=e116]: Welcome to PX360 Patient Experience Management
|
||||
- generic [ref=e117]:
|
||||
- button [ref=e119] [cursor=pointer]:
|
||||
- img [ref=e120]
|
||||
- generic [ref=e123]:
|
||||
- link "🇬🇧 English" [ref=e124] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=en
|
||||
- generic [ref=e125]: 🇬🇧
|
||||
- generic [ref=e126]: English
|
||||
- link "🇸🇦 Arabic" [ref=e127] [cursor=pointer]:
|
||||
- /url: /core/set-language/?language=ar
|
||||
- generic [ref=e128]: 🇸🇦
|
||||
- generic [ref=e129]: Arabic
|
||||
- button "E2E PX Employee PX Employee E" [ref=e131] [cursor=pointer]:
|
||||
- generic [ref=e132]:
|
||||
- generic [ref=e133]: E2E PX Employee
|
||||
- generic [ref=e134]: PX Employee
|
||||
- generic [ref=e135]: E
|
||||
- img [ref=e136]
|
||||
- main [ref=e138]:
|
||||
- generic [ref=e139]:
|
||||
- generic [ref=e140]:
|
||||
- link "Appreciations" [ref=e141] [cursor=pointer]:
|
||||
- /url: /appreciation/
|
||||
- img [ref=e142]
|
||||
- generic [ref=e144]: Detail
|
||||
- generic [ref=e145]: Sent
|
||||
- generic [ref=e146]:
|
||||
- heading "Appreciation Detail" [level=1] [ref=e147]:
|
||||
- img [ref=e148]
|
||||
- text: Appreciation Detail
|
||||
- generic [ref=e151]: 2026-07-09 13:55 — E2E Test Hospital
|
||||
- navigation [ref=e152]:
|
||||
- button "Details" [ref=e153] [cursor=pointer]
|
||||
- button "AI Analysis" [ref=e154] [cursor=pointer]
|
||||
- button "Notes" [ref=e155] [cursor=pointer]
|
||||
- main [ref=e156]:
|
||||
- generic [ref=e158]:
|
||||
- generic [ref=e159]:
|
||||
- heading "Appreciation Message" [level=3] [ref=e160]:
|
||||
- img [ref=e161]
|
||||
- text: Appreciation Message
|
||||
- paragraph
|
||||
- paragraph [ref=e163]: thank
|
||||
- paragraph
|
||||
- generic [ref=e164]:
|
||||
- heading "Submitted By" [level=3] [ref=e165]:
|
||||
- img [ref=e166]
|
||||
- text: Submitted By
|
||||
- generic [ref=e169]:
|
||||
- generic [ref=e170]:
|
||||
- paragraph [ref=e171]: Name
|
||||
- paragraph [ref=e172]: ismail
|
||||
- generic [ref=e173]:
|
||||
- paragraph [ref=e174]: Phone
|
||||
- paragraph [ref=e175]: "0569874589"
|
||||
- generic [ref=e176]:
|
||||
- heading "Sent" [level=3] [ref=e177]:
|
||||
- img [ref=e178]
|
||||
- text: Sent
|
||||
- generic [ref=e182]:
|
||||
- paragraph [ref=e183]: Sent At
|
||||
- paragraph [ref=e184]: 2026-07-09 13:57
|
||||
- contentinfo [ref=e186]:
|
||||
- paragraph [ref=e187]:
|
||||
- text: Powered by
|
||||
- link "tenhal.sa" [ref=e188] [cursor=pointer]:
|
||||
- /url: https://tenhal.sa
|
||||
@ -1 +1 @@
|
||||
hh-dev:hh-dev-build-1783592335|1783592388
|
||||
hh-dev:hh-dev-build-1784025225|1784026493
|
||||
|
||||
@ -207,4 +207,4 @@ META_APP_SECRET = "b80750bd12ab7f1c21d7d0ca891ba5ab"
|
||||
META_REDIRECT_URI = "https://micha-nonparabolic-lovie.ngrok-free.dev/social/callback/META/"
|
||||
META_WEBHOOK_VERIFY_TOKEN = "random_secret_string_khanfaheed123456"
|
||||
|
||||
EMAIL_LOGO_URL = "http://127.0.0.1:8000/static/img/HH_R_H_Logo.png"
|
||||
EMAIL_LOGO_URL = "http://127.0.0.1:8000/static/images/logos/HH_P_H_Logo_CMYK---LANDSCIP1.png"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
"""Forms for the appreciation module."""
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.appreciation.models import Appreciation
|
||||
from apps.organizations.models import Department
|
||||
@ -8,13 +9,25 @@ from apps.organizations.models import Department
|
||||
class AppreciationForm(forms.ModelForm):
|
||||
"""Internal form for creating an appreciation (department-targeted)."""
|
||||
|
||||
national_id = forms.CharField(
|
||||
label=_("National ID/Iqama No."),
|
||||
max_length=10,
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={
|
||||
"class": "w-full px-4 py-3 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy/20 outline-none",
|
||||
"placeholder": _("National ID or Iqama number"),
|
||||
"inputmode": "numeric",
|
||||
"pattern": "[0-9]{10}",
|
||||
"maxlength": "10",
|
||||
"minlength": "10",
|
||||
}),
|
||||
help_text=_("Exactly 10 digits, numbers only."),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Appreciation
|
||||
fields = ["hospital", "department", "message_en"]
|
||||
fields = ["department", "message_en", "national_id"]
|
||||
widgets = {
|
||||
"hospital": forms.Select(
|
||||
attrs={"class": "w-full px-4 py-3 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy/20 outline-none"}
|
||||
),
|
||||
"message_en": forms.Textarea(
|
||||
attrs={
|
||||
"rows": 5,
|
||||
@ -27,14 +40,21 @@ class AppreciationForm(forms.ModelForm):
|
||||
),
|
||||
}
|
||||
|
||||
def clean_national_id(self):
|
||||
national_id = self.cleaned_data.get("national_id", "").replace(" ", "")
|
||||
if not national_id.isdigit():
|
||||
raise forms.ValidationError(_("National ID/Iqama must contain digits only."))
|
||||
if len(national_id) != 10:
|
||||
raise forms.ValidationError(_("National ID/Iqama must be exactly 10 digits (you entered %(n)d).") % {"n": len(national_id)})
|
||||
return national_id
|
||||
|
||||
def __init__(self, *args, hospital=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if hospital:
|
||||
self.fields["hospital"].initial = hospital
|
||||
dept_qs = Department.objects.filter(status="active").select_related("hospital").order_by(
|
||||
"hospital__name", "name"
|
||||
)
|
||||
dept_qs = Department.objects.filter(hospital=hospital, status="active").order_by("name")
|
||||
else:
|
||||
dept_qs = Department.objects.filter(status="active").select_related("hospital").order_by("name")
|
||||
self.fields["department"].label_from_instance = lambda obj: (
|
||||
f"{obj.name} — {obj.hospital.name}" if obj.hospital else obj.name
|
||||
)
|
||||
self.fields["department"].queryset = dept_qs
|
||||
self.fields["department"].label_from_instance = lambda obj: (
|
||||
f"{obj.name} — {obj.hospital.name}" if obj.hospital else obj.name
|
||||
)
|
||||
|
||||
23
apps/appreciation/migrations/0007_add_reminder_fields.py
Normal file
23
apps/appreciation/migrations/0007_add_reminder_fields.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-13 16:35
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('appreciation', '0006_appreciation_reference_number_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='appreciation',
|
||||
name='dept_response_reminder_sent_at',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='appreciation',
|
||||
name='dept_response_second_reminder_sent_at',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,21 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def convert_ai_analyzed_to_activated(apps, schema_editor):
|
||||
Appreciation = apps.get_model("appreciation", "Appreciation")
|
||||
Appreciation.objects.filter(status="ai_analyzed").update(status="activated")
|
||||
|
||||
|
||||
def reverse_conversion(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("appreciation", "0007_add_reminder_fields"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(convert_ai_analyzed_to_activated, reverse_conversion),
|
||||
]
|
||||
24
apps/appreciation/migrations/0009_add_national_id.py
Normal file
24
apps/appreciation/migrations/0009_add_national_id.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-18 15:10
|
||||
|
||||
import apps.core.encryption
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('appreciation', '0008_remove_ai_analyzed_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='appreciation',
|
||||
name='national_id',
|
||||
field=apps.core.encryption.EncryptedCharField(blank=True, help_text='National ID/Iqama of the submitter', max_length=512),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='appreciation',
|
||||
name='national_id_hash',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=64),
|
||||
),
|
||||
]
|
||||
@ -12,6 +12,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelatio
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
|
||||
from apps.core.encryption import EncryptedCharField, compute_national_id_hash
|
||||
from apps.core.models import SoftDeleteModel, TimeStampedModel, UUIDModel
|
||||
|
||||
|
||||
@ -220,6 +221,14 @@ class Appreciation(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
notification_sent = models.BooleanField(default=False)
|
||||
notification_sent_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
# Reminder tracking
|
||||
dept_response_reminder_sent_at = models.DateTimeField(null=True, blank=True)
|
||||
dept_response_second_reminder_sent_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
# Submitter identity
|
||||
national_id = EncryptedCharField(max_length=512, blank=True, help_text="National ID/Iqama of the submitter")
|
||||
national_id_hash = models.CharField(max_length=64, blank=True, db_index=True)
|
||||
|
||||
# Metadata
|
||||
metadata = models.JSONField(default=dict, blank=True)
|
||||
|
||||
@ -245,6 +254,10 @@ class Appreciation(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
from apps.core.reference import generate_reference
|
||||
|
||||
self.reference_number = generate_reference("APR", self.hospital)
|
||||
if self.national_id:
|
||||
self.national_id_hash = compute_national_id_hash(self.national_id)
|
||||
else:
|
||||
self.national_id_hash = ""
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_owner(self):
|
||||
@ -320,20 +333,16 @@ class Appreciation(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
def mark_ai_analyzed(self, analysis_data):
|
||||
from django.utils import timezone
|
||||
|
||||
if self.status != AppreciationStatus.ACTIVATED:
|
||||
raise ValueError(f"Cannot mark as AI analyzed from '{self.status}' status. Must be in 'activated'.")
|
||||
|
||||
self.status = AppreciationStatus.AI_ANALYZED
|
||||
self.ai_analyzed_at = timezone.now()
|
||||
self.ai_analysis = analysis_data
|
||||
self.save(update_fields=["status", "ai_analyzed_at", "ai_analysis"])
|
||||
self.save(update_fields=["ai_analyzed_at", "ai_analysis"])
|
||||
|
||||
def send(self):
|
||||
"""Send appreciation and trigger notification"""
|
||||
from django.utils import timezone
|
||||
|
||||
if self.status not in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED):
|
||||
raise ValueError(f"Cannot send appreciation in '{self.status}' status. Must be 'activated' or 'ai_analyzed'.")
|
||||
if self.status not in (AppreciationStatus.ACTIVATED,):
|
||||
raise ValueError(f"Cannot send appreciation in '{self.status}' status. Must be 'activated'.")
|
||||
|
||||
self.status = AppreciationStatus.SENT
|
||||
self.sent_at = timezone.now()
|
||||
|
||||
@ -15,10 +15,17 @@ def send_appreciation_notifications(
|
||||
email_body="",
|
||||
host="",
|
||||
):
|
||||
"""Send email + SMS to champion, manager, and appreciated staff in the background.
|
||||
"""Send the appreciation email to the appreciated staff member with the
|
||||
department champion and manager CC'd.
|
||||
|
||||
Replaces the synchronous notification loop that used to block the
|
||||
appreciation_send_to response.
|
||||
Behaviour:
|
||||
- If ``staff_id`` is provided AND the staff member has an email: ONE email
|
||||
is sent To: the staff member with champion + manager as CC: (deduplicated).
|
||||
- If no staff member is selected (or the staff has no email): separate
|
||||
emails are sent To: each of the champion and manager (current behaviour).
|
||||
- In-app notifications are created for every recipient (To and CC) whose
|
||||
email maps to a User account.
|
||||
- SMS notifications are NOT sent (email-only flow).
|
||||
"""
|
||||
from apps.appreciation.models import Appreciation
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
@ -40,20 +47,95 @@ def send_appreciation_notifications(
|
||||
link = f"https://{host}/appreciation/detail/{appreciation.pk}/"
|
||||
notified = []
|
||||
|
||||
# Notify champion + manager
|
||||
for target in targets:
|
||||
display_name = (
|
||||
target["staff"].get_full_name() if target.get("staff")
|
||||
else (target["user"].get_full_name() if target.get("user") else target["label"])
|
||||
# Generate the appreciation letter PDF once and reuse for all email notifications.
|
||||
# Wrapped in try/except so email delivery still succeeds if PDF rendering fails.
|
||||
pdf_attachment = None
|
||||
try:
|
||||
from apps.appreciation.ui_views import _render_appreciation_pdf_bytes, _appreciation_pdf_context
|
||||
|
||||
ctx = _appreciation_pdf_context(appreciation)
|
||||
pdf_bytes = _render_appreciation_pdf_bytes(
|
||||
"appreciation/appreciation_letter.html", ctx, obj=appreciation,
|
||||
)
|
||||
if target.get("email"):
|
||||
pdf_attachment = (
|
||||
f"appreciation_letter_{appreciation.reference_number}.pdf",
|
||||
pdf_bytes,
|
||||
"application/pdf",
|
||||
)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"Failed to generate appreciation letter PDF for {appreciation_id}; "
|
||||
f"sending notifications without attachment"
|
||||
)
|
||||
|
||||
# Build the champion + manager email list (in order: champion first, then manager)
|
||||
target_emails_raw = [t["email"] for t in targets if t.get("email")]
|
||||
|
||||
# Resolve the appreciated staff member (if any) and their email
|
||||
staff_email = None
|
||||
appreciated_staff = None
|
||||
if staff_id:
|
||||
try:
|
||||
appreciated_staff = Staff.objects.get(id=staff_id)
|
||||
s_email, _ = _staff_contact(appreciated_staff)
|
||||
staff_email = s_email or None
|
||||
except Staff.DoesNotExist:
|
||||
appreciated_staff = None
|
||||
|
||||
# Build the deduplicated CC list (exclude the staff member's own email)
|
||||
seen_lower = {staff_email.lower()} if staff_email else set()
|
||||
cc_emails = []
|
||||
for email in target_emails_raw:
|
||||
key = email.lower()
|
||||
if key not in seen_lower:
|
||||
seen_lower.add(key)
|
||||
cc_emails.append(email)
|
||||
|
||||
attachments_arg = [pdf_attachment] if pdf_attachment else None
|
||||
|
||||
if staff_email:
|
||||
# CASE 1 (primary): ONE email to the staff member, CC champion + manager.
|
||||
send_subject = email_subject or f"An Appreciation Has Been Submitted About You - {appreciation.reference_number}"
|
||||
send_body = email_body or (
|
||||
f"An appreciation (#{appreciation.reference_number}) has been submitted about you."
|
||||
)
|
||||
try:
|
||||
NotificationService.send_email(
|
||||
email=staff_email,
|
||||
subject=send_subject,
|
||||
message=send_body + f"\n\n{link}",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">You've Been Appreciated!</h2>
|
||||
<p>An appreciation <strong>#{appreciation.reference_number}</strong> has been submitted about you.</p>
|
||||
<p><strong>Message:</strong> {appreciation.message_en or 'N/A'}</p>
|
||||
{f'<p><strong>Note:</strong> {note}</p>' if note else ''}
|
||||
<p><a href="{link}">View Appreciation</a></p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=appreciation,
|
||||
attachments=attachments_arg,
|
||||
cc=cc_emails,
|
||||
)
|
||||
notified.append(staff_email)
|
||||
for cc_addr in cc_emails:
|
||||
notified.append(f"CC: {cc_addr}")
|
||||
except Exception:
|
||||
logger.exception(f"Failed to send staff email to {staff_email} with CC {cc_emails}")
|
||||
|
||||
elif cc_emails:
|
||||
# CASE 2 (fallback): no specific staff — separate emails to champion + manager.
|
||||
send_subject = email_subject or f"Appreciation Sent to Department - {appreciation.reference_number}"
|
||||
send_body = email_body or (
|
||||
f"Appreciation #{appreciation.reference_number} has been sent to your department ({department.name})."
|
||||
)
|
||||
for email in cc_emails:
|
||||
try:
|
||||
send_subject = email_subject or f"Appreciation Sent to Department - {appreciation.reference_number}"
|
||||
send_body = email_body or (
|
||||
f"Appreciation #{appreciation.reference_number} has been sent to your department ({department.name})."
|
||||
)
|
||||
NotificationService.send_email(
|
||||
email=target["email"],
|
||||
email=email,
|
||||
subject=send_subject,
|
||||
message=send_body + f"\n\n{link}",
|
||||
html_message=f"""
|
||||
@ -64,67 +146,57 @@ def send_appreciation_notifications(
|
||||
<p>Appreciation <strong>#{appreciation.reference_number}</strong> has been sent to your department <strong>({department.name})</strong>.</p>
|
||||
<p><strong>Message:</strong> {appreciation.message_en or 'N/A'}</p>
|
||||
{f'<p><strong>Note:</strong> {note}</p>' if note else ''}
|
||||
<p><strong>Role:</strong> {target['label']}</p>
|
||||
<p><a href="{link}">View Appreciation</a></p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=appreciation,
|
||||
attachments=attachments_arg,
|
||||
)
|
||||
notified.append(email)
|
||||
except Exception:
|
||||
logger.exception(f"Failed to send email to {target.get('email')}")
|
||||
if target.get("phone"):
|
||||
try:
|
||||
NotificationService.send_sms(
|
||||
target["phone"],
|
||||
f"PX360: Appreciation #{appreciation.reference_number} sent to {department.name}. {link}",
|
||||
related_object=appreciation,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
notified.append(f"{display_name} ({target['label']})")
|
||||
logger.exception(f"Failed to send department email to {email}")
|
||||
|
||||
# Notify the appreciated staff member
|
||||
if staff_id:
|
||||
try:
|
||||
appreciated_staff = Staff.objects.get(id=staff_id)
|
||||
except Staff.DoesNotExist:
|
||||
appreciated_staff = None
|
||||
|
||||
if appreciated_staff:
|
||||
s_email, s_phone = _staff_contact(appreciated_staff)
|
||||
s_name = appreciated_staff.get_full_name()
|
||||
if s_email:
|
||||
try:
|
||||
NotificationService.send_email(
|
||||
email=s_email,
|
||||
subject=f"An Appreciation Has Been Submitted About You - {appreciation.reference_number}",
|
||||
message=f"An appreciation (#{appreciation.reference_number}) has been submitted about you.\n\nMessage: {appreciation.message_en or 'N/A'}\n\nView: {link}",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">You've Been Appreciated!</h2>
|
||||
<p>An appreciation <strong>#{appreciation.reference_number}</strong> has been submitted about you.</p>
|
||||
<p><strong>Message:</strong> {appreciation.message_en or 'N/A'}</p>
|
||||
{f'<p><strong>Note:</strong> {note}</p>' if note else ''}
|
||||
<p><a href="{link}">View Appreciation</a></p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=appreciation,
|
||||
)
|
||||
notified.append(f"{s_name} (Staff)")
|
||||
except Exception:
|
||||
logger.exception(f"Failed to send email to staff {s_email}")
|
||||
if s_phone:
|
||||
try:
|
||||
NotificationService.send_sms(
|
||||
s_phone,
|
||||
f"PX360: An appreciation has been submitted about you. {link}",
|
||||
related_object=appreciation,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
logger.warning(
|
||||
f"Appreciation {appreciation_id}: no recipients to notify "
|
||||
f"(no staff email and no champion/manager emails)"
|
||||
)
|
||||
|
||||
logger.info(f"Appreciation {appreciation_id} notifications sent to: {', '.join(notified)}")
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_appreciation_reminder_email(appreciation_id, reminder_type, recipients):
|
||||
"""Send a reminder email to department champion and manager for an appreciation."""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from apps.appreciation.models import Appreciation
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
|
||||
appreciation = Appreciation.objects.get(pk=appreciation_id)
|
||||
|
||||
for recipient in recipients:
|
||||
NotificationService.send_email(
|
||||
email=recipient["email"],
|
||||
subject=f"Reminder: Appreciation {appreciation.reference_number} - Acknowledgment Required",
|
||||
message=f"This is a reminder that appreciation {appreciation.reference_number} is awaiting acknowledgment. Please acknowledge it as soon as possible.",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Reminder: Acknowledgment Required</h2>
|
||||
<p style="margin: 0 0 12px 0;">This is a reminder that appreciation <strong>{appreciation.reference_number}</strong> is awaiting acknowledgment from your department.</p>
|
||||
<p style="margin: 0; color: #6b7280;">Please acknowledge it as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=appreciation,
|
||||
)
|
||||
|
||||
logger.info(f"Appreciation {appreciation_id} {reminder_type} reminder sent to {len(recipients)} recipients")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send appreciation reminder email for {appreciation_id}: {e}")
|
||||
|
||||
@ -41,8 +41,22 @@ def appreciation_list(request):
|
||||
).order_by("-created_at")
|
||||
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
if user.is_px_admin():
|
||||
pass
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_source_user():
|
||||
queryset = queryset.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
else:
|
||||
@ -61,16 +75,30 @@ def appreciation_list(request):
|
||||
)
|
||||
|
||||
base_qs = Appreciation.objects.all()
|
||||
if not user.is_px_admin() and user.hospital:
|
||||
if user.is_px_admin():
|
||||
if selected_hospital:
|
||||
base_qs = base_qs.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
base_qs = base_qs.filter(hospital=user.hospital)
|
||||
elif not user.is_px_admin():
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
base_qs = base_qs.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
base_qs = base_qs.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
base_qs = base_qs.filter(department=user.department)
|
||||
elif user.is_source_user():
|
||||
base_qs = base_qs.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
base_qs = base_qs.filter(hospital=user.hospital)
|
||||
else:
|
||||
base_qs = base_qs.none()
|
||||
|
||||
stats = {
|
||||
"total": base_qs.count(),
|
||||
"draft": base_qs.filter(status=AppreciationStatus.DRAFT).count(),
|
||||
"activated": base_qs.filter(
|
||||
status__in=[AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED]
|
||||
status=AppreciationStatus.ACTIVATED
|
||||
).count(),
|
||||
"sent": base_qs.filter(status__in=[AppreciationStatus.SENT, AppreciationStatus.ACKNOWLEDGED]).count(),
|
||||
}
|
||||
@ -132,6 +160,9 @@ def appreciation_detail(request, pk):
|
||||
appreciation_ct = ContentType.objects.get_for_model(appreciation)
|
||||
generic_notes = appreciation.notes.select_related("created_by").all()
|
||||
|
||||
has_recipient = appreciation.recipient is not None
|
||||
is_activated = appreciation.status == AppreciationStatus.ACTIVATED
|
||||
|
||||
context = {
|
||||
"appreciation": appreciation,
|
||||
"metadata": metadata,
|
||||
@ -140,13 +171,23 @@ def appreciation_detail(request, pk):
|
||||
"categories": categories,
|
||||
"hospital_departments": hospital_departments,
|
||||
"can_activate": appreciation.status == AppreciationStatus.DRAFT,
|
||||
"can_send": appreciation.status in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED),
|
||||
"can_send": is_activated and appreciation.department_id is not None,
|
||||
"has_recipient": has_recipient,
|
||||
"is_recipient": False,
|
||||
"content_type_id": appreciation_ct.pk,
|
||||
"object_id": appreciation.pk,
|
||||
"notes": generic_notes,
|
||||
"notes_count": generic_notes.count(),
|
||||
}
|
||||
_apr_steps = [
|
||||
{"label": _("Activate"), "icon": "zap", "done": appreciation.activated_at is not None},
|
||||
{"label": _("Select Staff"), "icon": "user-check", "done": has_recipient or appreciation.sent_at is not None},
|
||||
{"label": _("Send"), "icon": "send", "done": appreciation.sent_at is not None},
|
||||
]
|
||||
_apr_next = next((i for i, s in enumerate(_apr_steps) if not s["done"]), len(_apr_steps))
|
||||
if _apr_next < len(_apr_steps):
|
||||
_apr_steps[_apr_next]["current"] = True
|
||||
context["workflow_steps"] = _apr_steps
|
||||
return render(request, "appreciation/appreciation_detail.html", context)
|
||||
|
||||
|
||||
@ -165,17 +206,30 @@ def appreciation_create(request):
|
||||
|
||||
from .forms import AppreciationForm
|
||||
|
||||
hospital = user.hospital
|
||||
hospital = getattr(request, "tenant_hospital", None) or user.hospital
|
||||
|
||||
if request.method == "POST":
|
||||
form = AppreciationForm(request.POST, hospital=hospital)
|
||||
if form.is_valid():
|
||||
appreciation = form.save(commit=False)
|
||||
appreciation.sender = user
|
||||
appreciation.hospital = form.cleaned_data["hospital"]
|
||||
appreciation.hospital = hospital
|
||||
appreciation.status = AppreciationStatus.DRAFT
|
||||
appreciation.save()
|
||||
form.save_m2m()
|
||||
|
||||
staff_id = request.POST.get("staff_id")
|
||||
if staff_id:
|
||||
try:
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
staff_ct = ContentType.objects.get_for_model(Staff)
|
||||
staff = Staff.objects.get(pk=staff_id, status="active")
|
||||
appreciation.recipient_content_type = staff_ct
|
||||
appreciation.recipient_object_id = staff.id
|
||||
appreciation.save(update_fields=["recipient_content_type", "recipient_object_id"])
|
||||
except Staff.DoesNotExist:
|
||||
pass
|
||||
|
||||
messages.success(request, _("Appreciation created successfully."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
else:
|
||||
@ -261,12 +315,71 @@ def appreciation_activate(request, pk):
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def appreciation_select_recipient(request, pk):
|
||||
"""Select the department and optional staff recipient for an appreciation (without sending)."""
|
||||
appreciation = get_object_or_404(Appreciation, pk=pk)
|
||||
|
||||
if appreciation.status != AppreciationStatus.ACTIVATED:
|
||||
messages.error(request, _("Appreciation must be activated before selecting a recipient."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
user = request.user
|
||||
if not (
|
||||
user.is_px_admin() or user.is_hospital_admin()
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
):
|
||||
messages.error(request, _("Permission denied."))
|
||||
return redirect("appreciation:appreciation_list")
|
||||
|
||||
department_id = request.POST.get("department_id")
|
||||
staff_id = request.POST.get("staff_id")
|
||||
|
||||
if request.POST.get("clear_recipient"):
|
||||
appreciation.department = None
|
||||
appreciation.recipient_content_type = None
|
||||
appreciation.recipient_object_id = None
|
||||
appreciation.save(update_fields=["department", "recipient_content_type", "recipient_object_id"])
|
||||
messages.info(request, _("Recipient cleared. Please select a new recipient."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
if not department_id:
|
||||
messages.error(request, _("Please select a department."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
try:
|
||||
department = Department.objects.get(id=department_id, status="active")
|
||||
except Department.DoesNotExist:
|
||||
messages.error(request, _("Invalid department selected."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
appreciation.department = department
|
||||
|
||||
if staff_id:
|
||||
try:
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
staff_ct = ContentType.objects.get_for_model(Staff)
|
||||
staff = Staff.objects.get(pk=staff_id, status="active")
|
||||
appreciation.recipient_content_type = staff_ct
|
||||
appreciation.recipient_object_id = staff.id
|
||||
except Staff.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
appreciation.recipient_content_type = None
|
||||
appreciation.recipient_object_id = None
|
||||
|
||||
appreciation.save()
|
||||
messages.success(request, _("Recipient selected: {}").format(department.name))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def appreciation_send(request, pk):
|
||||
appreciation = get_object_or_404(Appreciation, pk=pk)
|
||||
|
||||
if appreciation.status not in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED):
|
||||
if appreciation.status != AppreciationStatus.ACTIVATED:
|
||||
messages.error(request, _("This appreciation must be activated before sending."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
@ -341,7 +454,7 @@ def appreciation_send_to(request, pk):
|
||||
)
|
||||
|
||||
# Must be activated before it can be sent
|
||||
if appreciation.status not in (AppreciationStatus.ACTIVATED, AppreciationStatus.AI_ANALYZED):
|
||||
if appreciation.status != AppreciationStatus.ACTIVATED:
|
||||
return JsonResponse(
|
||||
{"success": False, "error": str(_("Activate this appreciation before sending it to a department."))},
|
||||
status=400,
|
||||
@ -435,7 +548,7 @@ def appreciation_send_to(request, pk):
|
||||
except Staff.DoesNotExist:
|
||||
pass
|
||||
|
||||
# Advance to SENT (validates status is ACTIVATED/AI_ANALYZED)
|
||||
# Advance to SENT (validates status is ACTIVATED)
|
||||
appreciation.send()
|
||||
|
||||
# Send notifications (email + SMS to champion, manager, staff) in the background
|
||||
@ -508,6 +621,79 @@ def appreciation_acknowledge(request, pk):
|
||||
return redirect('appreciation:appreciation_detail', pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def appreciation_send_dept_reminder(request, pk):
|
||||
"""Send a reminder to the department that hasn't acknowledged an appreciation."""
|
||||
appreciation = get_object_or_404(Appreciation, pk=pk)
|
||||
|
||||
user = request.user
|
||||
if not (
|
||||
user.is_px_admin() or user.is_hospital_admin()
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
):
|
||||
messages.error(request, _("You don't have permission to send reminders."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
if appreciation.status not in (AppreciationStatus.SENT, AppreciationStatus.ACKNOWLEDGED):
|
||||
messages.warning(request, _("Appreciation must be sent before sending reminders."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
dept = appreciation.department
|
||||
if not dept:
|
||||
messages.error(request, _("No department assigned."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
reminder_type = request.POST.get("reminder_type", "first")
|
||||
|
||||
if reminder_type == "first" and appreciation.dept_response_reminder_sent_at:
|
||||
messages.warning(request, _("First reminder already sent."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
if reminder_type == "second" and appreciation.dept_response_second_reminder_sent_at:
|
||||
messages.warning(request, _("Second reminder already sent."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
if reminder_type == "second" and not appreciation.dept_response_reminder_sent_at:
|
||||
messages.warning(request, _("Please send the first reminder before the second."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
try:
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
from apps.organizations.department_contacts import get_champion_and_manager
|
||||
|
||||
targets = get_champion_and_manager(dept)
|
||||
if not targets:
|
||||
messages.error(request, _(f"No champion or manager assigned to {dept.name}."))
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
recipients = []
|
||||
for target in targets:
|
||||
email = target.get("email") or ""
|
||||
if email:
|
||||
recipients.append({"email": email, "name": target.get("display_name", "")})
|
||||
|
||||
if reminder_type == "first":
|
||||
appreciation.dept_response_reminder_sent_at = timezone.now()
|
||||
else:
|
||||
appreciation.dept_response_second_reminder_sent_at = timezone.now()
|
||||
appreciation.save(update_fields=["dept_response_reminder_sent_at", "dept_response_second_reminder_sent_at"])
|
||||
|
||||
from apps.appreciation.tasks import send_appreciation_reminder_email
|
||||
send_appreciation_reminder_email.delay(str(appreciation.id), reminder_type, recipients)
|
||||
|
||||
AuditService.log_event(
|
||||
event_type="appreciation_reminder_sent",
|
||||
description=f"{reminder_type.capitalize()} reminder sent for appreciation {appreciation.reference_number} by {user.get_full_name()}",
|
||||
content_object=appreciation,
|
||||
)
|
||||
|
||||
messages.success(request, _(f"{reminder_type.capitalize()} reminder sent to {len(recipients)} recipient(s)."))
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send appreciation reminder: {e}")
|
||||
messages.error(request, _("Failed to send reminder."))
|
||||
|
||||
return redirect("appreciation:appreciation_detail", pk=pk)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# LEADERBOARD VIEWS
|
||||
# ============================================================================
|
||||
@ -533,8 +719,23 @@ def leaderboard_view(request):
|
||||
queryset = AppreciationStats.objects.filter(year=year, month=month)
|
||||
|
||||
# Apply RBAC
|
||||
if not user.is_px_admin() and user.hospital:
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
if user.is_px_admin():
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
else:
|
||||
queryset = queryset.none()
|
||||
|
||||
# Apply filters
|
||||
hospital_filter = request.GET.get('hospital')
|
||||
@ -669,7 +870,7 @@ def category_list(request):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to manage categories.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -715,7 +916,7 @@ def category_create(request):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to create categories.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -773,7 +974,7 @@ def category_edit(request, pk):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to edit categories.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -818,7 +1019,7 @@ def category_delete(request, pk):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to delete categories.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -853,7 +1054,7 @@ def badge_list(request):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to manage badges.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -899,7 +1100,7 @@ def badge_create(request):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to create badges.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -961,7 +1162,7 @@ def badge_edit(request, pk):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to edit badges.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -1008,7 +1209,7 @@ def badge_delete(request, pk):
|
||||
user = request.user
|
||||
|
||||
# Check permission
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to delete badges.")
|
||||
return redirect('appreciation:appreciation_list')
|
||||
|
||||
@ -1039,7 +1240,7 @@ def appreciation_restore(request, pk):
|
||||
"""Restore deleted appreciation"""
|
||||
user = request.user
|
||||
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to restore appreciation.")
|
||||
return redirect('config:deleted_items')
|
||||
|
||||
@ -1224,6 +1425,16 @@ def public_appreciation_submit(request):
|
||||
if not contact_name or not contact_phone or not message:
|
||||
return JsonResponse({"success": False, "message": "Name, phone, and message are required."}, status=400)
|
||||
|
||||
from apps.core.validators import validate_saudi_phone
|
||||
from django.core.exceptions import ValidationError
|
||||
try:
|
||||
validate_saudi_phone(contact_phone)
|
||||
except ValidationError:
|
||||
return JsonResponse(
|
||||
{"success": False, "message": "Please enter a valid Saudi mobile number (e.g. 05XXXXXXXX or +9665XXXXXXXX)."},
|
||||
status=400,
|
||||
)
|
||||
|
||||
if not hospital_id:
|
||||
return JsonResponse({"success": False, "message": "Please select a hospital."}, status=400)
|
||||
|
||||
@ -1273,13 +1484,143 @@ def public_appreciation_submit(request):
|
||||
logger.exception("ERROR in public_appreciation_submit")
|
||||
return JsonResponse({"success": False, "message": str(e)}, status=500)
|
||||
|
||||
def _appreciation_pdf_context(appreciation):
|
||||
"""Build the common context for the appreciation letter & certificate PDFs."""
|
||||
hospital = appreciation.hospital
|
||||
hospital_name = hospital.name if hospital else ""
|
||||
hospital_name_ar = getattr(hospital, "name_ar", "") if hospital else ""
|
||||
|
||||
recipient = appreciation.recipient
|
||||
recipient_name = appreciation.get_recipient_name() or "—"
|
||||
department_name = appreciation.department.get_localized_name() if appreciation.department else ""
|
||||
recipient_title = ""
|
||||
if recipient is not None:
|
||||
getter = getattr(recipient, "get_localized_job_title", None)
|
||||
if callable(getter):
|
||||
recipient_title = getter() or ""
|
||||
recipient_title = recipient_title or getattr(recipient, "job_title", "") or ""
|
||||
|
||||
meta = appreciation.metadata or {}
|
||||
is_patient = meta.get("source") == "public_form" or bool(meta.get("submitted_by_name"))
|
||||
patient_name = (meta.get("submitted_by_name") or "").strip()
|
||||
|
||||
# Signer: sender (unless anonymous), else the activating officer, else generic PX team.
|
||||
signer = appreciation.sender if (appreciation.sender and not appreciation.is_anonymous) else appreciation.activated_by
|
||||
signer_name = _("Al Hammadi Patient Experience")
|
||||
signer_role = _("Patient Experience Team")
|
||||
if signer:
|
||||
signer_name = signer.get_full_name() or signer_name
|
||||
role_names = signer.get_role_names() if hasattr(signer, "get_role_names") else []
|
||||
if role_names:
|
||||
signer_role = ", ".join(role_names)
|
||||
|
||||
date_dt = appreciation.sent_at or appreciation.activated_at or appreciation.created_at
|
||||
|
||||
return {
|
||||
"hospital_name": hospital_name,
|
||||
"hospital_name_ar": hospital_name_ar,
|
||||
"recipient_name": recipient_name,
|
||||
"recipient_title": recipient_title,
|
||||
"department_name": department_name,
|
||||
"is_patient": is_patient,
|
||||
"patient_name": patient_name,
|
||||
"message_en": appreciation.message_en or "",
|
||||
"message_ar": appreciation.message_ar or "",
|
||||
"has_ar": bool((appreciation.message_ar or "").strip()),
|
||||
"signer_name": signer_name,
|
||||
"signer_role": signer_role,
|
||||
"date_dt": date_dt,
|
||||
"reference": appreciation.reference_number or "",
|
||||
}
|
||||
|
||||
|
||||
def _render_appreciation_pdf_bytes(template_name, context, obj=None):
|
||||
"""Render a PDF template via WeasyPrint and return the raw PDF bytes.
|
||||
|
||||
When ``obj`` is provided, its hospital letterhead is injected into the context
|
||||
as ``letterhead_path`` (data URI) via the shared ``pdf_utils`` helper.
|
||||
Used by both the PDF download views (wraps bytes in HttpResponse) and the
|
||||
notification task (attaches bytes to email).
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from weasyprint import HTML
|
||||
|
||||
full_context = dict(context)
|
||||
if obj is not None:
|
||||
from apps.core.pdf_utils import _get_letterhead_data_uri
|
||||
full_context.setdefault("letterhead_path", _get_letterhead_data_uri(obj))
|
||||
|
||||
html_string = render_to_string(template_name, full_context)
|
||||
return HTML(string=html_string, base_url=str(settings.BASE_DIR / "static")).write_pdf()
|
||||
|
||||
|
||||
def _appreciation_pdf_response(template_name, context, filename, obj=None):
|
||||
"""Render a PDF template via WeasyPrint and return it as an attachment response."""
|
||||
from django.http import HttpResponse
|
||||
|
||||
pdf_file = _render_appreciation_pdf_bytes(template_name, context, obj=obj)
|
||||
response = HttpResponse(pdf_file, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
return response
|
||||
|
||||
|
||||
def _check_appreciation_pdf_access(request, appreciation):
|
||||
"""Shared permission + activation gate for the appreciation PDF views."""
|
||||
user = request.user
|
||||
allowed = (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
or (user.hospital_id and appreciation.hospital_id == user.hospital_id)
|
||||
)
|
||||
if not allowed:
|
||||
messages.error(request, _("You don't have permission to view this appreciation."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
if not appreciation.activated_at:
|
||||
messages.error(request, _("This appreciation must be activated before generating a document."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
if not appreciation.recipient:
|
||||
messages.error(request, _("Select a recipient before generating a document."))
|
||||
return redirect("appreciation:appreciation_detail", pk=appreciation.pk)
|
||||
return None
|
||||
|
||||
|
||||
@login_required
|
||||
def appreciation_pdf(request, pk):
|
||||
from apps.core.pdf_utils import generate_letterhead_pdf
|
||||
def appreciation_letter(request, pk):
|
||||
"""Everyday Letter of Appreciation (portrait, message-forward, sender-signed)."""
|
||||
obj = get_object_or_404(Appreciation, id=pk)
|
||||
return generate_letterhead_pdf(
|
||||
'appreciation/appreciation_pdf.html',
|
||||
{'object': obj},
|
||||
f'appreciation_{obj.reference_number}.pdf',
|
||||
denied = _check_appreciation_pdf_access(request, obj)
|
||||
if denied:
|
||||
return denied
|
||||
ctx = _appreciation_pdf_context(obj)
|
||||
return _appreciation_pdf_response(
|
||||
"appreciation/appreciation_letter.html",
|
||||
ctx,
|
||||
f"appreciation_letter_{obj.reference_number}.pdf",
|
||||
obj=obj,
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def appreciation_certificate(request, pk):
|
||||
"""Formal Certificate of Appreciation (landscape, leadership-signed)."""
|
||||
obj = get_object_or_404(Appreciation, id=pk)
|
||||
denied = _check_appreciation_pdf_access(request, obj)
|
||||
if denied:
|
||||
return denied
|
||||
ctx = _appreciation_pdf_context(obj)
|
||||
hospital = obj.hospital
|
||||
ctx["ceo_name"] = hospital.ceo.get_full_name() if hospital and hospital.ceo else ""
|
||||
ctx["medical_director_name"] = (
|
||||
hospital.medical_director.get_full_name() if hospital and hospital.medical_director else ""
|
||||
)
|
||||
ctx["hospital_initials"] = (
|
||||
"".join(w[0] for w in (hospital.name or "").split()[:2]).upper() if hospital and hospital.name else "HH"
|
||||
)
|
||||
return _appreciation_pdf_response(
|
||||
"appreciation/appreciation_certificate.html",
|
||||
ctx,
|
||||
f"appreciation_certificate_{obj.reference_number}.pdf",
|
||||
)
|
||||
|
||||
@ -34,8 +34,10 @@ urlpatterns = [
|
||||
path('new/', ui_views.appreciation_create, name='appreciation_create'),
|
||||
path('detail/<uuid:pk>/', ui_views.appreciation_detail, name='appreciation_detail'),
|
||||
path('detail/<uuid:pk>/activate/', ui_views.appreciation_activate, name='appreciation_activate'),
|
||||
path('detail/<uuid:pk>/select-recipient/', ui_views.appreciation_select_recipient, name='appreciation_select_recipient'),
|
||||
path('detail/<uuid:pk>/send/', ui_views.appreciation_send, name='appreciation_send'),
|
||||
path('detail/<uuid:pk>/send-to/', ui_views.appreciation_send_to, name='appreciation_send_to'),
|
||||
path('detail/<uuid:pk>/send-reminder/', ui_views.appreciation_send_dept_reminder, name='appreciation_send_dept_reminder'),
|
||||
path('acknowledge/<uuid:pk>/', ui_views.appreciation_acknowledge, name='appreciation_acknowledge'),
|
||||
path('leaderboard/', ui_views.leaderboard_view, name='leaderboard_view'),
|
||||
path('badges/', ui_views.my_badges_view, name='my_badges_view'),
|
||||
@ -64,5 +66,7 @@ urlpatterns = [
|
||||
|
||||
# Public submission (no auth required)
|
||||
path('public/submit/', ui_views.public_appreciation_submit, name='public_appreciation_submit'),
|
||||
path('detail/<uuid:pk>/pdf/', ui_views.appreciation_pdf, name='appreciation_pdf'),
|
||||
# PDF downloads (require activation)
|
||||
path('detail/<uuid:pk>/letter/', ui_views.appreciation_letter, name='appreciation_letter'),
|
||||
path('detail/<uuid:pk>/certificate/', ui_views.appreciation_certificate, name='appreciation_certificate'),
|
||||
]
|
||||
|
||||
@ -398,7 +398,7 @@ class AppreciationBadgeViewSet(viewsets.ModelViewSet):
|
||||
|
||||
queryset = AppreciationBadge.objects.all()
|
||||
serializer_class = AppreciationBadgeSerializer
|
||||
permission_classes = [IsAuthenticated, IsPXAdminOrHospitalAdmin]
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter badges by hospital"""
|
||||
|
||||
@ -26,6 +26,7 @@ from apps.complaints.models import (
|
||||
)
|
||||
from apps.core.models import PriorityChoices, SeverityChoices
|
||||
from apps.core.form_mixins import HospitalFieldMixin, DepartmentFieldMixin
|
||||
from apps.core.validators import SAUDI_PHONE_HTML_PATTERN, validate_saudi_phone
|
||||
from apps.organizations.models import Area, Department, Hospital, LocationType, Patient, Staff, Section
|
||||
|
||||
|
||||
@ -93,7 +94,12 @@ class PublicComplaintForm(forms.ModelForm):
|
||||
label=_("Mobile Number"),
|
||||
max_length=20,
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": _("Your mobile number")}),
|
||||
widget=forms.TextInput(attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": _("05XXXXXXXX or +9665XXXXXXXX"),
|
||||
"pattern": SAUDI_PHONE_HTML_PATTERN,
|
||||
"inputmode": "tel",
|
||||
}),
|
||||
)
|
||||
|
||||
# Patient Information
|
||||
@ -280,16 +286,10 @@ class PublicComplaintForm(forms.ModelForm):
|
||||
).order_by("name_en")
|
||||
|
||||
def clean_mobile_number(self):
|
||||
"""Validate mobile number format"""
|
||||
"""Validate Saudi mobile number format (05xxxxxxxx / 5xxxxxxxx / +9665xxxxxxxx / 9665xxxxxxxx)."""
|
||||
mobile_number = self.cleaned_data.get("mobile_number")
|
||||
|
||||
# Remove spaces and dashes
|
||||
mobile_number = mobile_number.replace(" ", "").replace("-", "")
|
||||
|
||||
# Validate Saudi mobile format (05xxxxxxxx)
|
||||
if not mobile_number.startswith("05") or len(mobile_number) != 10:
|
||||
raise ValidationError(_("Please enter a valid Saudi mobile number (10 digits starting with 05)"))
|
||||
|
||||
if mobile_number:
|
||||
validate_saudi_phone(mobile_number)
|
||||
return mobile_number
|
||||
|
||||
def clean_national_id(self):
|
||||
@ -711,6 +711,7 @@ class InquiryForm(HospitalFieldMixin, forms.ModelForm):
|
||||
"contact_name",
|
||||
"contact_phone",
|
||||
"contact_email",
|
||||
"national_id",
|
||||
"section",
|
||||
"priority",
|
||||
"source",
|
||||
@ -763,6 +764,24 @@ class InquiryForm(HospitalFieldMixin, forms.ModelForm):
|
||||
department_id=department_id
|
||||
).order_by("name_en")
|
||||
|
||||
def clean_area(self):
|
||||
area = self.cleaned_data.get("area")
|
||||
if not area:
|
||||
return None
|
||||
return area
|
||||
|
||||
def clean_department(self):
|
||||
dept = self.cleaned_data.get("department")
|
||||
if not dept:
|
||||
return None
|
||||
return dept
|
||||
|
||||
def clean_section(self):
|
||||
section = self.cleaned_data.get("section")
|
||||
if not section:
|
||||
return None
|
||||
return section
|
||||
|
||||
|
||||
class SLAConfigForm(HospitalFieldMixin, forms.ModelForm):
|
||||
"""Form for creating and editing SLA configurations"""
|
||||
@ -869,7 +888,13 @@ class PublicInquiryForm(forms.Form):
|
||||
label=_("Phone Number"),
|
||||
max_length=20,
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": _("Your phone number")}),
|
||||
validators=[validate_saudi_phone],
|
||||
widget=forms.TextInput(attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": _("05XXXXXXXX or +9665XXXXXXXX"),
|
||||
"pattern": SAUDI_PHONE_HTML_PATTERN,
|
||||
"inputmode": "tel",
|
||||
}),
|
||||
)
|
||||
|
||||
email = forms.EmailField(
|
||||
@ -878,6 +903,13 @@ class PublicInquiryForm(forms.Form):
|
||||
widget=forms.EmailInput(attrs={"class": "form-control", "placeholder": _("your@email.com")}),
|
||||
)
|
||||
|
||||
national_id = forms.CharField(
|
||||
label=_("National ID/Iqama"),
|
||||
max_length=20,
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": _("National ID or Iqama (optional)")}),
|
||||
)
|
||||
|
||||
# Inquiry Details
|
||||
hospital = forms.ModelChoiceField(
|
||||
label=_("Hospital"),
|
||||
|
||||
18
apps/complaints/migrations/0043_alter_inquiry_status.py
Normal file
18
apps/complaints/migrations/0043_alter_inquiry_status.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-15 15:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('complaints', '0042_inquiry_closed_by_inquiry_resolved_by'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='inquiry',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('open', 'Open'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('closed', 'Closed'), ('contact_no_response', 'Contacted - No Response')], db_index=True, default='open', max_length=25),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,24 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-18 14:46
|
||||
|
||||
import apps.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('complaints', '0043_alter_inquiry_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='complaint',
|
||||
name='contact_phone',
|
||||
field=models.CharField(blank=True, max_length=20, validators=[apps.core.validators.validate_saudi_phone]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inquiry',
|
||||
name='contact_phone',
|
||||
field=models.CharField(blank=True, max_length=20, validators=[apps.core.validators.validate_saudi_phone]),
|
||||
),
|
||||
]
|
||||
24
apps/complaints/migrations/0045_inquiry_add_national_id.py
Normal file
24
apps/complaints/migrations/0045_inquiry_add_national_id.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-19 06:23
|
||||
|
||||
import apps.core.encryption
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('complaints', '0044_alter_complaint_contact_phone_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='inquiry',
|
||||
name='national_id',
|
||||
field=apps.core.encryption.EncryptedCharField(blank=True, help_text='National ID/Iqama of the contact', max_length=512),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='inquiry',
|
||||
name='national_id_hash',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=64),
|
||||
),
|
||||
]
|
||||
@ -16,9 +16,10 @@ from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.core.encryption import EncryptedCharField, compute_national_id_hash, mask_national_id
|
||||
|
||||
from apps.core.models import PriorityChoices, SeverityChoices, SoftDeleteModel, TenantModel, TimeStampedModel, UUIDModel
|
||||
from apps.core.validators import validate_saudi_phone
|
||||
from apps.organizations.models import LocationType
|
||||
|
||||
|
||||
@ -213,7 +214,7 @@ class Complaint(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
|
||||
# Contact information for anonymous/unregistered submissions
|
||||
contact_name = models.CharField(max_length=200, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True, validators=[validate_saudi_phone])
|
||||
contact_email = models.EmailField(blank=True)
|
||||
|
||||
# Public complaint form fields
|
||||
@ -1539,8 +1540,10 @@ class Inquiry(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
|
||||
# Contact information (if patient not in system)
|
||||
contact_name = models.CharField(max_length=200, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True, validators=[validate_saudi_phone])
|
||||
contact_email = models.EmailField(blank=True)
|
||||
national_id = EncryptedCharField(max_length=512, blank=True, help_text="National ID/Iqama of the contact")
|
||||
national_id_hash = models.CharField(max_length=64, blank=True, db_index=True)
|
||||
|
||||
# Organization
|
||||
hospital = models.ForeignKey("organizations.Hospital", on_delete=models.CASCADE, related_name="inquiries")
|
||||
@ -1699,6 +1702,7 @@ class Inquiry(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
("in_progress", _("In Progress")),
|
||||
("resolved", _("Resolved")),
|
||||
("closed", _("Closed")),
|
||||
("contact_no_response", _("Contacted - No Response")),
|
||||
],
|
||||
default="open",
|
||||
db_index=True,
|
||||
@ -2024,11 +2028,27 @@ class Inquiry(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
else:
|
||||
self.timeline_sla = "more_than_72_hours"
|
||||
|
||||
if self.national_id:
|
||||
self.national_id_hash = compute_national_id_hash(self.national_id)
|
||||
else:
|
||||
self.national_id_hash = ""
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.subject} ({self.status})"
|
||||
|
||||
@property
|
||||
def handling_department_id(self):
|
||||
"""The department currently responsible for handling this inquiry.
|
||||
|
||||
When the inquiry has been forwarded, this is the receiving department
|
||||
(``transferred_to_department``); otherwise it's the primary ``department``.
|
||||
Used to gate department actions so a department that has forwarded an
|
||||
inquiry away can no longer act on it — only the current handler can.
|
||||
"""
|
||||
return self.transferred_to_department_id or self.department_id
|
||||
|
||||
def get_absolute_url(self):
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
@ -3069,26 +3069,36 @@ def _apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis):
|
||||
|
||||
inquiry.priority = analysis.get("priority", inquiry.priority)
|
||||
|
||||
department_name = analysis.get("department", "")
|
||||
if department_name:
|
||||
if department := Department.objects.filter(
|
||||
hospital_id=inquiry.hospital.id, name__iexact=department_name, status="active"
|
||||
).first():
|
||||
inquiry.department = department
|
||||
logger.info(f"Matched department exactly: {department.name}")
|
||||
elif department := Department.objects.filter(
|
||||
hospital_id=inquiry.hospital.id, name__icontains=department_name, status="active"
|
||||
).first():
|
||||
inquiry.department = department
|
||||
logger.info(f"Matched department partially: {department.name} from '{department_name}'")
|
||||
# Department auto-detection — skip when the user already provided one
|
||||
# (quick-create modal sets metadata.ai_skip_department). For those, AI only
|
||||
# fills subject + priority, never overriding the user's department.
|
||||
if not (inquiry.metadata or {}).get("ai_skip_department"):
|
||||
department_name = analysis.get("department", "")
|
||||
if department_name:
|
||||
if department := Department.objects.filter(
|
||||
hospital_id=inquiry.hospital.id, name__iexact=department_name, status="active"
|
||||
).first():
|
||||
inquiry.department = department
|
||||
logger.info(f"Matched department exactly: {department.name}")
|
||||
elif department := Department.objects.filter(
|
||||
hospital_id=inquiry.hospital.id, name__icontains=department_name, status="active"
|
||||
).first():
|
||||
inquiry.department = department
|
||||
logger.info(f"Matched department partially: {department.name} from '{department_name}'")
|
||||
|
||||
if analysis.get("brief_summary_en"):
|
||||
inquiry.ai_brief_en = analysis["brief_summary_en"]
|
||||
if analysis.get("brief_summary_ar"):
|
||||
inquiry.ai_brief_ar = analysis["brief_summary_ar"]
|
||||
|
||||
# For quick-created inquiries (no user-authored subject), let the AI title
|
||||
# become the subject. Guarded by a metadata flag so user subjects are never
|
||||
# overwritten for inquiries created via the full form.
|
||||
if not inquiry.metadata:
|
||||
inquiry.metadata = {}
|
||||
if inquiry.metadata.get("subject_pending_ai") and analysis.get("title_en"):
|
||||
inquiry.subject = analysis["title_en"][:500]
|
||||
inquiry.metadata["subject_pending_ai"] = False
|
||||
|
||||
inquiry.metadata["ai_analysis"] = {
|
||||
"title_en": analysis.get("title_en", ""),
|
||||
@ -3123,6 +3133,7 @@ def _apply_inquiry_ai_analysis(inquiry, analysis, emotion_analysis):
|
||||
|
||||
inquiry.save(
|
||||
update_fields=[
|
||||
"subject",
|
||||
"priority",
|
||||
"department",
|
||||
"ai_brief_en",
|
||||
@ -3696,3 +3707,39 @@ def send_department_notification_task(payload):
|
||||
f"{emailed} emailed, {smsed} smsed"
|
||||
)
|
||||
return {"emailed": emailed, "smsed": smsed}
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_inquiry_reminder_email(inquiry_id, reminder_type, recipients):
|
||||
"""Send a reminder email to department champion and manager for an inquiry."""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from apps.complaints.models import Inquiry
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
|
||||
inquiry = Inquiry.objects.get(pk=inquiry_id)
|
||||
|
||||
for recipient in recipients:
|
||||
NotificationService.send_email(
|
||||
email=recipient["email"],
|
||||
subject=f"Reminder: Inquiry #{inquiry.reference_number} - Response Required",
|
||||
message=f"This is a reminder that inquiry #{inquiry.reference_number} is awaiting your department's response. Please submit your response as soon as possible.",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Reminder: Response Required</h2>
|
||||
<p style="margin: 0 0 12px 0;">This is a reminder that inquiry <strong>#{inquiry.reference_number}</strong> is awaiting your department's response.</p>
|
||||
<p style="margin: 0; color: #6b7280;">Please submit your response as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=inquiry,
|
||||
)
|
||||
|
||||
logger.info(f"Inquiry {inquiry_id} {reminder_type} reminder sent to {len(recipients)} recipients")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send inquiry reminder email for {inquiry_id}: {e}")
|
||||
|
||||
@ -25,6 +25,25 @@ from apps.organizations.models import Department, Hospital, Staff
|
||||
from apps.observations.models import Observation
|
||||
from apps.px_sources.models import SourceUser, PXSource
|
||||
|
||||
|
||||
def _safe_next_redirect(request, fallback):
|
||||
"""Redirect to request.POST['next'] if it's a safe relative URL.
|
||||
|
||||
Blocks open-redirect attacks. A URL is considered safe only if it starts
|
||||
with a single leading slash and is not a protocol-relative form (``//``
|
||||
or ``/\\``) which browsers may resolve against a different host.
|
||||
Falls back to ``fallback`` (a redirect()/reverse() result) otherwise.
|
||||
"""
|
||||
next_url = request.POST.get("next") if hasattr(request, "POST") else None
|
||||
if (
|
||||
next_url
|
||||
and next_url.startswith("/")
|
||||
and not next_url.startswith("//")
|
||||
and not next_url.startswith("/\\")
|
||||
):
|
||||
return redirect(next_url)
|
||||
return fallback
|
||||
|
||||
from .models import (
|
||||
Complaint,
|
||||
ComplaintAttachment,
|
||||
@ -330,6 +349,9 @@ def complaint_list(request):
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
@ -877,9 +899,7 @@ def complaint_detail(request, pk):
|
||||
context["notes"] = complaint.notes.select_related("created_by").all()
|
||||
context["notes_count"] = context["notes"].count()
|
||||
|
||||
# Datastar variant for testing: /complaints/<pk>/?ds=1 renders complaint_detail_ds.html
|
||||
template = "complaints/complaint_detail_ds.html" if request.GET.get("ds") else "complaints/complaint_detail.html"
|
||||
return render(request, template, context)
|
||||
return render(request, "complaints/complaint_detail.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
@ -1462,6 +1482,18 @@ def complaint_send_dept_reminder(request, pk):
|
||||
)
|
||||
department = involved_dept.department
|
||||
|
||||
reminder_type = request.POST.get("reminder_type", "first")
|
||||
|
||||
if reminder_type == "first" and involved_dept.first_reminder_sent_at:
|
||||
messages.warning(request, _("First reminder already sent to this department."))
|
||||
return redirect("complaints:complaint_detail", pk=pk)
|
||||
if reminder_type == "second" and involved_dept.second_reminder_sent_at:
|
||||
messages.warning(request, _("Second reminder already sent to this department."))
|
||||
return redirect("complaints:complaint_detail", pk=pk)
|
||||
if reminder_type == "second" and not involved_dept.first_reminder_sent_at:
|
||||
messages.warning(request, _("Please send the first reminder before the second."))
|
||||
return redirect("complaints:complaint_detail", pk=pk)
|
||||
|
||||
targets = get_champion_and_manager(department)
|
||||
if not targets:
|
||||
messages.error(
|
||||
@ -1522,10 +1554,16 @@ def complaint_send_dept_reminder(request, pk):
|
||||
notified.append(f"{display_name} ({label})")
|
||||
|
||||
if sent_any:
|
||||
if reminder_type == "first":
|
||||
involved_dept.first_reminder_sent_at = timezone.now()
|
||||
else:
|
||||
involved_dept.second_reminder_sent_at = timezone.now()
|
||||
involved_dept.save(update_fields=["first_reminder_sent_at", "second_reminder_sent_at"])
|
||||
|
||||
ComplaintUpdate.objects.create(
|
||||
complaint=complaint,
|
||||
update_type="communication",
|
||||
message=f"Manual reminder sent to {department.get_localized_name()} — {', '.join(notified)}",
|
||||
message=f"Manual {reminder_type} reminder sent to {department.get_localized_name()} — {', '.join(notified)}",
|
||||
created_by=user,
|
||||
metadata={
|
||||
"event_type": "manual_reminder",
|
||||
@ -2762,7 +2800,7 @@ def inquiry_list(request):
|
||||
from .models import Inquiry
|
||||
|
||||
# Base queryset with optimizations
|
||||
queryset = Inquiry.objects.select_related("patient", "hospital", "department", "assigned_to", "responded_by")
|
||||
queryset = Inquiry.objects.select_related("patient", "hospital", "department", "outgoing_department", "assigned_to", "responded_by")
|
||||
|
||||
# Apply RBAC filters
|
||||
user = request.user
|
||||
@ -2774,6 +2812,9 @@ def inquiry_list(request):
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(
|
||||
Q(department=user.department) | Q(outgoing_department=user.department)
|
||||
@ -2789,33 +2830,55 @@ def inquiry_list(request):
|
||||
else:
|
||||
queryset = queryset.none()
|
||||
|
||||
# Apply filters
|
||||
# Snapshot the RBAC base before direction/filters so we can compute
|
||||
# incoming/outgoing counts for the tabs.
|
||||
base_qs = queryset
|
||||
|
||||
# Apply non-direction filters (status, category, hospital, department, search)
|
||||
status_filter = request.GET.get("status")
|
||||
if status_filter:
|
||||
queryset = queryset.filter(status=status_filter)
|
||||
base_qs = base_qs.filter(status=status_filter)
|
||||
|
||||
category_filter = request.GET.get("category")
|
||||
if category_filter:
|
||||
queryset = queryset.filter(category=category_filter)
|
||||
base_qs = base_qs.filter(category=category_filter)
|
||||
|
||||
hospital_filter = request.GET.get("hospital")
|
||||
if hospital_filter:
|
||||
queryset = queryset.filter(hospital_id=hospital_filter)
|
||||
base_qs = base_qs.filter(hospital_id=hospital_filter)
|
||||
|
||||
department_filter = request.GET.get("department")
|
||||
if department_filter:
|
||||
queryset = queryset.filter(department_id=department_filter)
|
||||
base_qs = base_qs.filter(department_id=department_filter)
|
||||
|
||||
# Search
|
||||
search_query = request.GET.get("search")
|
||||
if search_query:
|
||||
queryset = queryset.filter(
|
||||
base_qs = base_qs.filter(
|
||||
Q(subject__icontains=search_query)
|
||||
| Q(message__icontains=search_query)
|
||||
| Q(contact_name__icontains=search_query)
|
||||
| Q(contact_email__icontains=search_query)
|
||||
)
|
||||
|
||||
# Direction split: incoming vs outgoing
|
||||
direction = request.GET.get("direction", "incoming")
|
||||
dept = getattr(user, "department", None)
|
||||
is_dept_scoped = user.is_department_manager() or user.is_champion()
|
||||
if is_dept_scoped and dept:
|
||||
sent_away = Q(department=dept) & Q(transferred_to_department__isnull=False) & ~Q(transferred_to_department=dept)
|
||||
incoming_qs = base_qs.filter(
|
||||
Q(department=dept) | Q(transferred_to_department=dept) | Q(outgoing_department=dept)
|
||||
).exclude(sent_away)
|
||||
outgoing_qs = base_qs.filter(sent_away)
|
||||
else:
|
||||
# PX roles: incoming = not forwarded; outgoing = forwarded to a department
|
||||
incoming_qs = base_qs.exclude(transferred_to_department__isnull=False)
|
||||
outgoing_qs = base_qs.filter(transferred_to_department__isnull=False)
|
||||
|
||||
incoming_count = incoming_qs.count()
|
||||
outgoing_count = outgoing_qs.count()
|
||||
queryset = outgoing_qs if direction == "outgoing" else incoming_qs
|
||||
|
||||
# Ordering
|
||||
order_by = request.GET.get("order_by", "-created_at")
|
||||
queryset = queryset.order_by(order_by)
|
||||
@ -2850,6 +2913,9 @@ def inquiry_list(request):
|
||||
"stats": stats,
|
||||
"departments": departments,
|
||||
"filters": request.GET,
|
||||
"direction": direction,
|
||||
"incoming_count": incoming_count,
|
||||
"outgoing_count": outgoing_count,
|
||||
}
|
||||
|
||||
return render(request, "complaints/inquiry_list.html", context)
|
||||
@ -2884,7 +2950,11 @@ def inquiry_detail(request, pk):
|
||||
# Check access
|
||||
user = request.user
|
||||
if not user.is_px_admin():
|
||||
if user.is_hospital_admin() and inquiry.hospital != user.hospital:
|
||||
if user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital and inquiry.hospital != user.hospital:
|
||||
messages.error(request, "You don't have permission to view this inquiry.")
|
||||
return redirect("inquiries:inquiry_list")
|
||||
elif user.is_hospital_admin() and inquiry.hospital != user.hospital:
|
||||
messages.error(request, "You don't have permission to view this inquiry.")
|
||||
return redirect("inquiries:inquiry_list")
|
||||
elif (
|
||||
@ -3146,6 +3216,151 @@ def inquiry_send_to_staff(request, pk):
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def inquiry_quick_create(request):
|
||||
"""Quick-create an inquiry from the simple modal (PX staff only).
|
||||
|
||||
Fields: message, contact name, phone (required), email (optional),
|
||||
location type, category, department, section. The inquiry is auto-activated
|
||||
and assigned to the current user. AI fills subject (from its title) and
|
||||
priority in the background; the user's department/section/category are kept.
|
||||
"""
|
||||
from .models import Inquiry, InquiryUpdate
|
||||
|
||||
user = request.user
|
||||
if not (
|
||||
user.is_px_admin() or user.is_hospital_admin()
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
):
|
||||
return JsonResponse(
|
||||
{"success": False, "error": str(_("You don't have permission to create inquiries."))},
|
||||
status=403,
|
||||
)
|
||||
|
||||
message = (request.POST.get("message") or "").strip()
|
||||
contact_name = (request.POST.get("contact_name") or "").strip()
|
||||
contact_phone = (request.POST.get("contact_phone") or "").strip()
|
||||
contact_email = (request.POST.get("contact_email") or "").strip()
|
||||
location_type = (request.POST.get("location_type") or "").strip()
|
||||
department_id = (request.POST.get("department") or "").strip()
|
||||
section_id = (request.POST.get("section") or "").strip()
|
||||
|
||||
# Required-field validation (the inquiry-type `category` is intentionally NOT
|
||||
# collected here — the model field is required at DB level so it is defaulted
|
||||
# to "general" below. The department-category in the modal is a UI filter only.)
|
||||
missing = {
|
||||
"message": message,
|
||||
"contact_name": contact_name,
|
||||
"contact_phone": contact_phone,
|
||||
"location_type": location_type,
|
||||
"department": department_id,
|
||||
}
|
||||
for field, value in missing.items():
|
||||
if not value:
|
||||
return JsonResponse(
|
||||
{"success": False, "error": str(_("This field is required: {}").format(field))},
|
||||
status=400,
|
||||
)
|
||||
|
||||
hospital = getattr(request, "tenant_hospital", None) or user.hospital
|
||||
if not hospital:
|
||||
return JsonResponse({"success": False, "error": str(_("No hospital assigned to your account."))}, status=400)
|
||||
|
||||
# Validate location_type
|
||||
from apps.complaints.models import Inquiry as _Inquiry
|
||||
valid_location_types = {c for c, _ in _Inquiry._meta.get_field("location_type").choices or []}
|
||||
if valid_location_types and location_type not in valid_location_types:
|
||||
return JsonResponse({"success": False, "error": str(_("Invalid location type."))}, status=400)
|
||||
|
||||
# Validate department belongs to the hospital
|
||||
from apps.organizations.models import Department, Section
|
||||
try:
|
||||
department = Department.objects.get(id=department_id, hospital=hospital, status="active")
|
||||
except Department.DoesNotExist:
|
||||
return JsonResponse({"success": False, "error": str(_("Invalid department for this hospital."))}, status=400)
|
||||
|
||||
# Section is optional — only validate if provided
|
||||
section = None
|
||||
if section_id:
|
||||
try:
|
||||
section = Section.objects.get(id=section_id, department=department)
|
||||
except Section.DoesNotExist:
|
||||
return JsonResponse(
|
||||
{"success": False, "error": str(_("Selected section does not belong to this department."))},
|
||||
status=400,
|
||||
)
|
||||
|
||||
try:
|
||||
inquiry = Inquiry.objects.create(
|
||||
hospital=hospital,
|
||||
created_by=user,
|
||||
message=message,
|
||||
contact_name=contact_name,
|
||||
contact_phone=contact_phone,
|
||||
contact_email=contact_email,
|
||||
location_type=location_type,
|
||||
category="general",
|
||||
department=department,
|
||||
section=section,
|
||||
# The inquiry is routed to this department for handling — mark it as
|
||||
# sent so it appears in the department's incoming queue (not outgoing).
|
||||
sent_to_department=True,
|
||||
sent_to_department_at=timezone.now(),
|
||||
# Defaults — AI refines subject/priority in the background (NOT department)
|
||||
subject=message[:80],
|
||||
priority="medium",
|
||||
# Auto-activate + auto-assign to the creator
|
||||
status="in_progress",
|
||||
assigned_to=user,
|
||||
assigned_at=timezone.now(),
|
||||
activated_at=timezone.now(),
|
||||
metadata={
|
||||
"subject_pending_ai": True, # AI fills the subject from its generated title
|
||||
"ai_skip_department": True, # user provided department — AI must not override it
|
||||
},
|
||||
)
|
||||
|
||||
# Background AI analysis (fills subject/priority/department) + notifications
|
||||
from apps.complaints.tasks import analyze_inquiry_with_ai, notify_staff_new_item
|
||||
analyze_inquiry_with_ai.delay(str(inquiry.id))
|
||||
notify_staff_new_item.delay("inquiry", str(inquiry.id))
|
||||
|
||||
InquiryUpdate.objects.create(
|
||||
inquiry=inquiry,
|
||||
update_type="note",
|
||||
message="Inquiry created.",
|
||||
created_by=user,
|
||||
)
|
||||
roles_display = ", ".join(user.get_role_names()) if hasattr(user, "get_role_names") else ""
|
||||
InquiryUpdate.objects.create(
|
||||
inquiry=inquiry,
|
||||
update_type="assignment",
|
||||
message=f"Inquiry auto-activated and assigned to {user.get_full_name()} ({roles_display})",
|
||||
created_by=user,
|
||||
)
|
||||
|
||||
AuditService.log_event(
|
||||
event_type="inquiry_created",
|
||||
description=f"Quick inquiry created: {inquiry.subject}",
|
||||
user=user,
|
||||
content_object=inquiry,
|
||||
metadata={"category": inquiry.category, "quick_create": True},
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
"success": True,
|
||||
"inquiry_id": str(inquiry.id),
|
||||
"reference_number": inquiry.reference_number,
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Quick inquiry create failed: {e}")
|
||||
return JsonResponse(
|
||||
{"success": False, "error": str(_("An error occurred while creating the inquiry."))},
|
||||
status=500,
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def inquiry_create(request):
|
||||
@ -3431,6 +3646,8 @@ def inquiry_assign(request, pk):
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or (user.is_department_manager() and user.department and user.department == inquiry.department)
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
)
|
||||
if not can_assign:
|
||||
messages.error(request, "You don't have permission to assign inquiries.")
|
||||
@ -3509,24 +3726,37 @@ def inquiry_change_status(request, pk):
|
||||
if not (
|
||||
user.is_px_admin() or user.is_hospital_admin()
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
or (user.is_department_manager() and user.department_id == inquiry.handling_department_id)
|
||||
or (user.is_champion() and user.department_id == inquiry.handling_department_id)
|
||||
):
|
||||
messages.error(request, "You don't have permission to change inquiry status.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
return _safe_next_redirect(request, redirect("inquiries:inquiry_detail", pk=pk))
|
||||
|
||||
new_status = request.POST.get("status")
|
||||
note = request.POST.get("note", "")
|
||||
|
||||
if not new_status:
|
||||
messages.error(request, "Please select a status.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
return _safe_next_redirect(request, redirect("inquiries:inquiry_detail", pk=pk))
|
||||
|
||||
old_status = inquiry.status
|
||||
inquiry.status = new_status
|
||||
|
||||
# Handle status-specific logic
|
||||
if new_status == "resolved" and not inquiry.response:
|
||||
messages.error(request, "Please add a response before resolving.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
# Department users (champion / dept manager) must record a patient contact
|
||||
# outcome before closing/resolving an inquiry. PX roles follow their own flow.
|
||||
if new_status in ("resolved", "closed") and (user.is_champion() or user.is_department_manager()):
|
||||
if inquiry.contact_status not in ("contacted", "contacted_no_response"):
|
||||
messages.error(request, _("Please record a patient contact outcome before closing."))
|
||||
return _safe_next_redirect(request, redirect("inquiries:inquiry_detail", pk=pk))
|
||||
|
||||
# "Contacted - No Response" is a terminal status set when the department could
|
||||
# not reach the patient. Auto-sync the contact_status tracking so outreach data
|
||||
# stays consistent without double data entry.
|
||||
if new_status == "contact_no_response":
|
||||
inquiry.contact_status = "contacted_no_response"
|
||||
inquiry.contact_status_at = timezone.now()
|
||||
inquiry.contact_status_by = user
|
||||
|
||||
inquiry._acting_user = request.user
|
||||
inquiry.save()
|
||||
@ -3551,7 +3781,67 @@ def inquiry_change_status(request, pk):
|
||||
)
|
||||
|
||||
messages.success(request, f"Inquiry status changed to {new_status}.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
return _safe_next_redirect(request, redirect("inquiries:inquiry_detail", pk=pk))
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def inquiry_set_contact_status(request, pk):
|
||||
"""Record the outcome of the department's patient-contact attempt.
|
||||
|
||||
Sets the existing ``contact_status`` field (not_contacted / contacted /
|
||||
contacted_no_response). Used by the department (champion/manager) that owns
|
||||
the inquiry to log communication outcomes with the patient.
|
||||
"""
|
||||
from .models import Inquiry, InquiryUpdate
|
||||
|
||||
inquiry = get_object_or_404(Inquiry, pk=pk)
|
||||
|
||||
user = request.user
|
||||
if not (
|
||||
user.is_px_admin() or user.is_hospital_admin()
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
or (user.is_department_manager() and user.department_id == inquiry.handling_department_id)
|
||||
or (user.is_champion() and user.department_id == inquiry.handling_department_id)
|
||||
):
|
||||
messages.error(request, _("You don't have permission to set the contact status."))
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
new_contact_status = request.POST.get("contact_status")
|
||||
note = request.POST.get("note", "").strip()
|
||||
|
||||
valid = {c for c, _ in Inquiry._meta.get_field("contact_status").choices}
|
||||
if new_contact_status not in valid:
|
||||
messages.error(request, _("Please select a valid contact status."))
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
old_contact_status = inquiry.contact_status
|
||||
inquiry.contact_status = new_contact_status
|
||||
inquiry.contact_status_at = timezone.now()
|
||||
inquiry.contact_status_by = user
|
||||
inquiry.save(update_fields=["contact_status", "contact_status_at", "contact_status_by"])
|
||||
|
||||
display = dict(Inquiry._meta.get_field("contact_status").choices).get(new_contact_status, new_contact_status)
|
||||
InquiryUpdate.objects.create(
|
||||
inquiry=inquiry,
|
||||
update_type="communication",
|
||||
message=note or _("Contact status set to: {status}").format(status=display),
|
||||
created_by=user,
|
||||
)
|
||||
|
||||
AuditService.log_event(
|
||||
event_type="inquiry_contact_status",
|
||||
description=_("Contact status changed from {old} to {new}").format(
|
||||
old=old_contact_status or "—", new=new_contact_status
|
||||
),
|
||||
user=user,
|
||||
content_object=inquiry,
|
||||
metadata={"old_contact_status": old_contact_status, "new_contact_status": new_contact_status},
|
||||
)
|
||||
|
||||
messages.success(request, _("Contact status updated to: {status}").format(status=display))
|
||||
|
||||
return _safe_next_redirect(request, redirect("inquiries:inquiry_detail", pk=pk))
|
||||
|
||||
|
||||
@login_required
|
||||
@ -3566,6 +3856,8 @@ def inquiry_reopen(request, pk):
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or (user.is_department_manager() and user.department == inquiry.department)
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
):
|
||||
messages.error(request, "You don't have permission to reopen inquiries.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
@ -3643,6 +3935,8 @@ def inquiry_respond(request, pk):
|
||||
and user.department
|
||||
and user.department in [inquiry.department, inquiry.outgoing_department]
|
||||
)
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
)
|
||||
if not can_respond:
|
||||
messages.error(request, "You don't have permission to respond to inquiries.")
|
||||
@ -3755,8 +4049,9 @@ def inquiry_transfer_to_department(request, pk):
|
||||
if not (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_department_manager()
|
||||
or (user.is_department_manager() and user.department_id == inquiry.handling_department_id)
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
or (user.is_champion() and user.department_id == inquiry.handling_department_id)
|
||||
):
|
||||
messages.error(request, _("You don't have permission to transfer inquiries to departments."))
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
@ -4000,8 +4295,9 @@ def inquiry_send_to(request, pk):
|
||||
if not (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_department_manager()
|
||||
or (user.is_department_manager() and user.department_id == inquiry.handling_department_id)
|
||||
or user.is_px_management() or user.is_px_employee()
|
||||
or (user.is_champion() and user.department_id == inquiry.handling_department_id)
|
||||
):
|
||||
return JsonResponse({
|
||||
"success": False,
|
||||
@ -4097,9 +4393,8 @@ def inquiry_send_to(request, pk):
|
||||
inquiry.transferred_to_department = department
|
||||
inquiry.transfer_count = (inquiry.transfer_count or 0) + 1
|
||||
|
||||
if inquiry.department_id == department.pk:
|
||||
inquiry.sent_to_department = True
|
||||
inquiry.sent_to_department_at = timezone.now()
|
||||
inquiry.sent_to_department = True
|
||||
inquiry.sent_to_department_at = timezone.now()
|
||||
|
||||
sla_config = inquiry.get_sla_config()
|
||||
if sla_config and sla_config.dept_response_hours:
|
||||
@ -4184,6 +4479,8 @@ def inquiry_department_response(request, pk):
|
||||
inquiry.department == user.department
|
||||
or inquiry.outgoing_department == user.department
|
||||
))
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
):
|
||||
messages.error(request, "You don't have permission to respond to this inquiry.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
@ -4243,28 +4540,6 @@ Generate a JSON response with:
|
||||
content_object=inquiry,
|
||||
)
|
||||
|
||||
# Notify inquirer that department has responded
|
||||
try:
|
||||
from apps.notifications.services import NotificationService
|
||||
from apps.core.utils import build_public_track_url
|
||||
|
||||
track_url = build_public_track_url("inquiry", inquiry.reference_number)
|
||||
if inquiry.contact_phone:
|
||||
NotificationService.send_sms(
|
||||
phone=inquiry.contact_phone,
|
||||
message=f"PX360: Your inquiry {inquiry.reference_number} has been responded to. View: {track_url}",
|
||||
related_object=inquiry,
|
||||
)
|
||||
if inquiry.contact_email:
|
||||
NotificationService.send_email(
|
||||
email=inquiry.contact_email,
|
||||
subject=f"PX360: Response to Your Inquiry {inquiry.reference_number}",
|
||||
message=f"Your inquiry has been responded to.\n\nView: {track_url}",
|
||||
related_object=inquiry,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to send inquirer notification: {e}")
|
||||
|
||||
messages.success(request, "Department response submitted successfully.")
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
from django.http import JsonResponse
|
||||
@ -4307,30 +4582,29 @@ def inquiry_send_dept_response_reminder(request, pk):
|
||||
|
||||
reminder_type = request.POST.get("reminder_type", "first")
|
||||
|
||||
if reminder_type == "first" and inquiry.dept_response_reminder_sent_at:
|
||||
messages.warning(request, "First reminder already sent.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
if reminder_type == "second" and inquiry.dept_response_second_reminder_sent_at:
|
||||
messages.warning(request, "Second reminder already sent.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
if reminder_type == "second" and not inquiry.dept_response_reminder_sent_at:
|
||||
messages.warning(request, "Please send the first reminder before the second.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
try:
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
from apps.organizations.department_contacts import get_champion_and_manager
|
||||
|
||||
targets = get_champion_and_manager(dept)
|
||||
if not targets:
|
||||
messages.error(request, f"No champion or manager assigned to {dept.name}.")
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
recipients = []
|
||||
if dept.champion and dept.champion.user and dept.champion.user.email:
|
||||
recipients.append(dept.champion.user)
|
||||
|
||||
for recipient in recipients:
|
||||
NotificationService.send_email(
|
||||
email=recipient.email,
|
||||
subject=f"Reminder: Inquiry #{inquiry.reference_number} - Response Required",
|
||||
message=f"This is a reminder that inquiry #{inquiry.reference_number} is awaiting your department's response. Please submit your response as soon as possible.",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Reminder: Response Required</h2>
|
||||
<p style="margin: 0 0 12px 0;">This is a reminder that inquiry <strong>#{inquiry.reference_number}</strong> is awaiting your department's response.</p>
|
||||
<p style="margin: 0; color: #6b7280;">Please submit your response as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=inquiry,
|
||||
)
|
||||
for target in targets:
|
||||
email = target.get("email") or ""
|
||||
if email:
|
||||
recipients.append({"email": email, "name": target.get("display_name", "")})
|
||||
|
||||
if reminder_type == "first":
|
||||
inquiry.dept_response_reminder_sent_at = timezone.now()
|
||||
@ -4338,6 +4612,9 @@ def inquiry_send_dept_response_reminder(request, pk):
|
||||
inquiry.dept_response_second_reminder_sent_at = timezone.now()
|
||||
inquiry.save()
|
||||
|
||||
from .tasks import send_inquiry_reminder_email
|
||||
send_inquiry_reminder_email.delay(str(inquiry.id), reminder_type, recipients)
|
||||
|
||||
InquiryUpdate.objects.create(
|
||||
inquiry=inquiry,
|
||||
update_type="note",
|
||||
@ -4400,7 +4677,7 @@ def inquiry_update_contact_stage(request, pk):
|
||||
inquiry = get_object_or_404(Inquiry, pk=pk)
|
||||
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or inquiry.assigned_to == user):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or inquiry.assigned_to == user or user.is_px_management() or user.is_px_employee()):
|
||||
messages.error(request, _("You don't have permission to update contact tracking."))
|
||||
return redirect("inquiries:inquiry_detail", pk=pk)
|
||||
|
||||
@ -4885,6 +5162,7 @@ def public_inquiry_submit(request):
|
||||
name = request.POST.get("name")
|
||||
email = request.POST.get("email")
|
||||
phone = request.POST.get("phone")
|
||||
national_id = request.POST.get("national_id", "")
|
||||
hospital_id = request.POST.get("hospital")
|
||||
location_type = request.POST.get("location_type", "")
|
||||
department_id = request.POST.get("department", "")
|
||||
@ -4949,6 +5227,7 @@ def public_inquiry_submit(request):
|
||||
contact_name=name,
|
||||
contact_phone=phone,
|
||||
contact_email=email,
|
||||
national_id=national_id,
|
||||
status="open",
|
||||
is_outgoing=False,
|
||||
)
|
||||
@ -6508,7 +6787,7 @@ def adverse_action_add(request, complaint_pk):
|
||||
"complaint": complaint,
|
||||
"action_type_choices": ComplaintAdverseAction.ActionType.choices,
|
||||
"severity_choices": ComplaintAdverseAction.SeverityLevel.choices,
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, is_active=True),
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, status="active"),
|
||||
},
|
||||
)
|
||||
|
||||
@ -6561,11 +6840,10 @@ def adverse_action_add(request, complaint_pk):
|
||||
"complaint": complaint,
|
||||
"action_type_choices": ComplaintAdverseAction.ActionType.choices,
|
||||
"severity_choices": ComplaintAdverseAction.SeverityLevel.choices,
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, is_active=True)
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, status="active")
|
||||
if complaint.hospital
|
||||
else Staff.objects.filter(is_active=True),
|
||||
else Staff.objects.filter(status="active"),
|
||||
}
|
||||
|
||||
return render(request, "complaints/adverse_action_form.html", context)
|
||||
|
||||
|
||||
@ -6623,9 +6901,9 @@ def adverse_action_edit(request, pk):
|
||||
"complaint": complaint,
|
||||
"action_type_choices": ComplaintAdverseAction.ActionType.choices,
|
||||
"severity_choices": ComplaintAdverseAction.SeverityLevel.choices,
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, is_active=True)
|
||||
"staff_list": Staff.objects.filter(hospital=complaint.hospital, status="active")
|
||||
if complaint.hospital
|
||||
else Staff.objects.filter(is_active=True),
|
||||
else Staff.objects.filter(status="active"),
|
||||
"selected_staff": list(adverse_action.involved_staff.values_list("id", flat=True)),
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,13 @@ urlpatterns = [
|
||||
# Inquiries UI Views
|
||||
path("", ui_views.inquiry_list, name="inquiry_list"),
|
||||
path("new/", ui_views.inquiry_create, name="inquiry_create"),
|
||||
path("new/quick/", ui_views.inquiry_quick_create, name="inquiry_quick_create"),
|
||||
path("<uuid:pk>/", ui_views.inquiry_detail, name="inquiry_detail"),
|
||||
path("<uuid:pk>/edit/", ui_views.inquiry_edit, name="inquiry_edit"),
|
||||
path("<uuid:pk>/activate/", ui_views.inquiry_activate, name="inquiry_activate"),
|
||||
path("<uuid:pk>/assign/", ui_views.inquiry_assign, name="inquiry_assign"),
|
||||
path("<uuid:pk>/change-status/", ui_views.inquiry_change_status, name="inquiry_change_status"),
|
||||
path("<uuid:pk>/contact-status/", ui_views.inquiry_set_contact_status, name="inquiry_set_contact_status"),
|
||||
path("<uuid:pk>/reopen/", ui_views.inquiry_reopen, name="inquiry_reopen"),
|
||||
path("<uuid:pk>/add-note/", ui_views.inquiry_add_note, name="inquiry_add_note"),
|
||||
path("<uuid:pk>/respond/", ui_views.inquiry_respond, name="inquiry_respond"),
|
||||
|
||||
@ -1721,7 +1721,7 @@ Provide 3-5 suggestions. Keep them concise and focused on how to respond to the
|
||||
user.is_px_admin()
|
||||
or (user.is_hospital_admin() and user.hospital == complaint.hospital)
|
||||
or (user.is_department_manager() and user.department == complaint.department)
|
||||
or (user.is_px_employee and complaint.hospital == user.hospital)
|
||||
or (user.is_px_employee() and complaint.hospital == user.hospital)
|
||||
)
|
||||
if not can_generate:
|
||||
return Response(
|
||||
@ -1804,7 +1804,7 @@ Provide 3-5 suggestions. Keep them concise and focused on how to respond to the
|
||||
user.is_px_admin()
|
||||
or (user.is_hospital_admin() and user.hospital == complaint.hospital)
|
||||
or (user.is_department_manager() and user.department == complaint.department)
|
||||
or (user.is_px_employee and complaint.hospital == user.hospital)
|
||||
or (user.is_px_employee() and complaint.hospital == user.hospital)
|
||||
)
|
||||
if not can_generate:
|
||||
return Response(
|
||||
@ -2992,6 +2992,8 @@ class InquiryViewSet(viewsets.ModelViewSet):
|
||||
user = request.user
|
||||
can_generate = (
|
||||
user.is_px_admin()
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
or (user.is_hospital_admin() and user.hospital == inquiry.hospital)
|
||||
or inquiry.assigned_to == user
|
||||
)
|
||||
@ -5210,28 +5212,6 @@ Generate JSON with "summary_en" and "summary_ar"."""
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Notify inquirer
|
||||
track_url = build_public_track_url("inquiry", inquiry.reference_number)
|
||||
if inquiry.contact_phone:
|
||||
try:
|
||||
NotificationService.send_sms(
|
||||
phone=inquiry.contact_phone,
|
||||
message=f"PX360: Your inquiry {inquiry.reference_number} has been responded to. View: {track_url}",
|
||||
related_object=inquiry,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if inquiry.contact_email:
|
||||
try:
|
||||
NotificationService.send_email(
|
||||
email=inquiry.contact_email,
|
||||
subject=f"PX360: Response to Your Inquiry {inquiry.reference_number}",
|
||||
message=f"Your inquiry has been responded to.\n\nView: {track_url}",
|
||||
related_object=inquiry,
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return render(request, "complaints/inquiry_response_success_token.html", {"inquiry": inquiry})
|
||||
|
||||
return render(request, "complaints/inquiry_response_form_token.html", {"inquiry": inquiry})
|
||||
|
||||
@ -1010,8 +1010,8 @@ class AIService:
|
||||
"title": title or "Complaint",
|
||||
"title_en": title or "Complaint",
|
||||
"title_ar": title or "شكوى",
|
||||
"short_description_en": description[:200] if description else "",
|
||||
"short_description_ar": description[:200] if description else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"severity": "medium",
|
||||
"priority": "medium",
|
||||
"category": "other",
|
||||
@ -1032,8 +1032,8 @@ class AIService:
|
||||
"title": title or "Complaint",
|
||||
"title_en": title or "Complaint",
|
||||
"title_ar": title or "شكوى",
|
||||
"short_description_en": description[:200] if description else "",
|
||||
"short_description_ar": description[:200] if description else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"severity": "medium",
|
||||
"priority": "medium",
|
||||
"category": "other",
|
||||
@ -1182,8 +1182,8 @@ Always use EXACT names and codes from the provided taxonomy - do not invent new
|
||||
"title_ar": subject,
|
||||
"brief_summary_en": "Inquiry",
|
||||
"brief_summary_ar": "استفسار",
|
||||
"short_description_en": message[:200] if message else "",
|
||||
"short_description_ar": message[:200] if message else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"priority": "medium",
|
||||
"department": "",
|
||||
"reasoning_en": "AI analysis failed, using default values",
|
||||
@ -1198,8 +1198,8 @@ Always use EXACT names and codes from the provided taxonomy - do not invent new
|
||||
"title_ar": subject,
|
||||
"brief_summary_en": "Inquiry",
|
||||
"brief_summary_ar": "استفسار",
|
||||
"short_description_en": message[:200] if message else "",
|
||||
"short_description_ar": message[:200] if message else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"priority": "medium",
|
||||
"department": "",
|
||||
"reasoning_en": f"AI service unavailable: {str(e)}",
|
||||
@ -1418,8 +1418,8 @@ Always use EXACT names and codes from the provided taxonomy - do not invent new
|
||||
"title_ar": title or "ملاحظة",
|
||||
"brief_summary_en": "Observation",
|
||||
"brief_summary_ar": "ملاحظة",
|
||||
"short_description_en": description[:200] if description else "",
|
||||
"short_description_ar": description[:200] if description else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"severity": "medium",
|
||||
"category": "Other",
|
||||
"department": "",
|
||||
@ -1439,8 +1439,8 @@ Always use EXACT names and codes from the provided taxonomy - do not invent new
|
||||
"title_ar": title or "ملاحظة",
|
||||
"brief_summary_en": "Observation",
|
||||
"brief_summary_ar": "ملاحظة",
|
||||
"short_description_en": description[:200] if description else "",
|
||||
"short_description_ar": description[:200] if description else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"severity": "medium",
|
||||
"category": "Other",
|
||||
"department": "",
|
||||
@ -2218,10 +2218,10 @@ Respond in JSON format:
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to parse suggestion AI response: {e}")
|
||||
return {
|
||||
"short_description_en": message[:200] if message else "",
|
||||
"short_description_ar": message[:200] if message else "",
|
||||
"description_en": message[:500] if message else "",
|
||||
"description_ar": message[:500] if message else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"description_en": "",
|
||||
"description_ar": "",
|
||||
"category": "other",
|
||||
"priority": "medium",
|
||||
"suggested_actions": [],
|
||||
@ -2233,10 +2233,10 @@ Respond in JSON format:
|
||||
except AIServiceError as e:
|
||||
logger.error(f"AI service error for suggestion: {e}")
|
||||
return {
|
||||
"short_description_en": message[:200] if message else "",
|
||||
"short_description_ar": message[:200] if message else "",
|
||||
"description_en": message[:500] if message else "",
|
||||
"description_ar": message[:500] if message else "",
|
||||
"short_description_en": "",
|
||||
"short_description_ar": "",
|
||||
"description_en": "",
|
||||
"description_ar": "",
|
||||
"category": "other",
|
||||
"priority": "medium",
|
||||
"suggested_actions": [],
|
||||
|
||||
@ -118,7 +118,9 @@ class DepartmentRespondentMiddleware(MiddlewareMixin):
|
||||
if not request.user.is_champion():
|
||||
return None
|
||||
|
||||
if request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_department_manager() or request.user.is_director():
|
||||
if (request.user.is_px_admin() or request.user.is_hospital_admin()
|
||||
or request.user.is_department_manager() or request.user.is_director()
|
||||
or request.user.is_px_employee() or request.user.is_px_management()):
|
||||
return None
|
||||
|
||||
if request.headers.get("x-requested-with") == "XMLHttpRequest":
|
||||
|
||||
45
apps/core/validators.py
Normal file
45
apps/core/validators.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
Shared validators used across multiple apps.
|
||||
"""
|
||||
import re
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Regex for a Saudi mobile number after stripping spaces/dashes/parens/plus.
|
||||
# Accepts any of:
|
||||
# 05XXXXXXXX (10 digits, local with trunk 0)
|
||||
# 5XXXXXXXX (9 digits, no trunk)
|
||||
# 9665XXXXXXXX (12 digits, country code without +)
|
||||
# +9665XXXXXXXX (13 digits, country code with +)
|
||||
SAUDI_MOBILE_REGEX = re.compile(r"^(?:\+?966|0)?5\d{8}$")
|
||||
|
||||
# HTML5 form pattern equivalent (HTML anchors ^...$ implicitly).
|
||||
# Exposed for reuse in widget attrs and template <input pattern="...">.
|
||||
SAUDI_PHONE_HTML_PATTERN = r"(\+?966|0)?5[0-9]{8}"
|
||||
|
||||
|
||||
def validate_saudi_phone(value):
|
||||
"""
|
||||
Validate that ``value`` is a Saudi mobile number.
|
||||
|
||||
Accepts the following formats (spaces, dashes and parentheses are ignored):
|
||||
05XXXXXXXX
|
||||
5XXXXXXXX
|
||||
9665XXXXXXXX
|
||||
+9665XXXXXXXX
|
||||
|
||||
Stored value is returned unchanged (no normalization).
|
||||
Raises ``ValidationError`` if the number is not a valid Saudi mobile.
|
||||
"""
|
||||
if value is None or value == "":
|
||||
return value
|
||||
|
||||
cleaned = re.sub(r"[\s\-()]", "", str(value))
|
||||
if not SAUDI_MOBILE_REGEX.match(cleaned):
|
||||
raise ValidationError(
|
||||
_("Please enter a valid Saudi mobile number "
|
||||
"(e.g. 05XXXXXXXX or +9665XXXXXXXX)."),
|
||||
code="invalid_saudi_phone",
|
||||
)
|
||||
return value
|
||||
@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.organizations.models import Department, Hospital, Patient, Staff
|
||||
from apps.core.form_mixins import HospitalFieldMixin
|
||||
from apps.core.validators import SAUDI_PHONE_HTML_PATTERN, validate_saudi_phone
|
||||
from .models import Feedback, FeedbackResponse, FeedbackStatus, FeedbackType, FeedbackCategory
|
||||
|
||||
|
||||
@ -259,7 +260,13 @@ class PublicSuggestionForm(forms.Form):
|
||||
label=_("Mobile Number"),
|
||||
max_length=20,
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": _("Your mobile number")}),
|
||||
validators=[validate_saudi_phone],
|
||||
widget=forms.TextInput(attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": _("05XXXXXXXX or +9665XXXXXXXX"),
|
||||
"pattern": SAUDI_PHONE_HTML_PATTERN,
|
||||
"inputmode": "tel",
|
||||
}),
|
||||
)
|
||||
contact_email = forms.EmailField(
|
||||
label=_("Email Address"),
|
||||
|
||||
26
apps/feedback/migrations/0009_add_activation_fields.py
Normal file
26
apps/feedback/migrations/0009_add_activation_fields.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-14 12:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feedback', '0008_feedback_reference_number_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feedback',
|
||||
name='activated_at',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feedback',
|
||||
name='activated_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='activated_feedbacks', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,19 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-18 14:46
|
||||
|
||||
import apps.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feedback', '0009_add_activation_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feedback',
|
||||
name='contact_phone',
|
||||
field=models.CharField(blank=True, max_length=20, validators=[apps.core.validators.validate_saudi_phone]),
|
||||
),
|
||||
]
|
||||
@ -15,6 +15,7 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.core.models import PriorityChoices, SoftDeleteModel, TimeStampedModel, UUIDModel
|
||||
from apps.core.validators import validate_saudi_phone
|
||||
|
||||
|
||||
class FeedbackType(models.TextChoices):
|
||||
@ -94,7 +95,7 @@ class Feedback(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
is_anonymous = models.BooleanField(default=False)
|
||||
contact_name = models.CharField(max_length=200, blank=True)
|
||||
contact_email = models.EmailField(blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True)
|
||||
contact_phone = models.CharField(max_length=20, blank=True, validators=[validate_saudi_phone])
|
||||
|
||||
encounter_id = models.CharField(
|
||||
max_length=100, blank=True, db_index=True, help_text="Related encounter ID if applicable"
|
||||
@ -196,6 +197,10 @@ class Feedback(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
status = models.CharField(
|
||||
max_length=20, choices=FeedbackStatus.choices, default=FeedbackStatus.SUBMITTED, db_index=True
|
||||
)
|
||||
activated_at = models.DateTimeField(null=True, blank=True)
|
||||
activated_by = models.ForeignKey(
|
||||
"accounts.User", on_delete=models.SET_NULL, null=True, blank=True, related_name="activated_feedbacks"
|
||||
)
|
||||
|
||||
# Assignment
|
||||
assigned_to = models.ForeignKey(
|
||||
|
||||
@ -17,6 +17,7 @@ urlpatterns = [
|
||||
path("<uuid:pk>/update/", views.feedback_update, name="feedback_update"),
|
||||
path("<uuid:pk>/delete/", views.feedback_delete, name="feedback_delete"),
|
||||
# Workflow actions
|
||||
path("<uuid:pk>/activate/", views.feedback_activate, name="feedback_activate"),
|
||||
path("<uuid:pk>/assign/", views.feedback_assign, name="feedback_assign"),
|
||||
path("<uuid:pk>/change-status/", views.feedback_change_status, name="feedback_change_status"),
|
||||
path("<uuid:pk>/send-to-department/", views.feedback_send_to_department, name="feedback_send_to_department"),
|
||||
|
||||
@ -259,11 +259,14 @@ def feedback_detail(request, pk):
|
||||
hospital=feedback.hospital, status="active"
|
||||
).order_by("name"),
|
||||
"status_choices": FeedbackStatus.choices,
|
||||
"can_activate": feedback.activated_at is None,
|
||||
"is_active": feedback.activated_at is not None,
|
||||
"can_edit": (
|
||||
user.is_px_admin()
|
||||
(user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
or user.is_px_employee())
|
||||
and feedback.activated_at is not None
|
||||
),
|
||||
"can_admin": user.is_px_admin() or user.is_hospital_admin(),
|
||||
"can_send_to_department": (
|
||||
@ -282,8 +285,8 @@ def feedback_detail(request, pk):
|
||||
# Workflow stepper
|
||||
_fb_steps = [
|
||||
{"label": "Submitted", "icon": "plus-circle", "done": True},
|
||||
{"label": "Assigned", "icon": "user-plus", "done": feedback.assigned_to is not None},
|
||||
{"label": "Reviewed", "icon": "eye", "done": feedback.status in ("reviewed", "acknowledged", "closed")},
|
||||
{"label": "Activated", "icon": "zap", "done": feedback.activated_at is not None},
|
||||
{"label": "Sent to Dept", "icon": "send", "done": feedback.department_id is not None or feedback.status in ("acknowledged", "closed")},
|
||||
{"label": "Acknowledged", "icon": "thumbs-up", "done": feedback.status in ("acknowledged", "closed")},
|
||||
{"label": "Closed", "icon": "check-circle-2", "done": feedback.status == "closed"},
|
||||
]
|
||||
@ -298,7 +301,6 @@ def feedback_detail(request, pk):
|
||||
@login_required
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def feedback_create(request):
|
||||
from apps.organizations.models import LegacyLocation, LegacyMainSection, LegacySubSection, Hospital
|
||||
from apps.feedback.models import FeedbackType, FeedbackCategory, FeedbackStatus
|
||||
|
||||
communication_request = None
|
||||
@ -313,11 +315,12 @@ def feedback_create(request):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
hospital = getattr(request, "tenant_hospital", None) or request.user.hospital
|
||||
|
||||
if request.method == "POST":
|
||||
contact_name = request.POST.get("contact_name", "").strip()
|
||||
contact_phone = request.POST.get("contact_phone", "").strip()
|
||||
message = request.POST.get("message", "").strip()
|
||||
hospital_id = request.POST.get("hospital", "")
|
||||
title = request.POST.get("title", message[:100]).strip()
|
||||
|
||||
errors = []
|
||||
@ -327,23 +330,15 @@ def feedback_create(request):
|
||||
errors.append("Phone is required")
|
||||
if not message:
|
||||
errors.append("Message is required")
|
||||
if not hospital_id:
|
||||
errors.append("Hospital is required")
|
||||
if hospital is None:
|
||||
errors.append("No hospital context. Contact your administrator.")
|
||||
|
||||
if errors:
|
||||
for e in errors:
|
||||
messages.error(request, e)
|
||||
else:
|
||||
try:
|
||||
hospital = Hospital.objects.get(id=hospital_id)
|
||||
|
||||
# Auto-link patient by phone (local first, then HIS). Never blocks creation.
|
||||
from apps.organizations.patient_lookup import find_or_link_patient
|
||||
|
||||
linked_patient = find_or_link_patient(phone=contact_phone, hospital=hospital)
|
||||
|
||||
feedback = Feedback(
|
||||
patient=linked_patient,
|
||||
hospital=hospital,
|
||||
feedback_type=FeedbackType.SUGGESTION,
|
||||
title=title,
|
||||
@ -365,10 +360,11 @@ def feedback_create(request):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from apps.feedback.tasks import analyze_suggestion_with_ai
|
||||
from apps.complaints.tasks import notify_staff_new_item
|
||||
from apps.feedback.tasks import analyze_suggestion_with_ai, link_feedback_patient
|
||||
analyze_suggestion_with_ai.delay(str(feedback.id))
|
||||
notify_staff_new_item.delay("suggestion", str(feedback.id))
|
||||
link_feedback_patient.delay(str(feedback.id), phone=contact_phone)
|
||||
|
||||
FeedbackResponse.objects.create(
|
||||
feedback=feedback,
|
||||
@ -400,9 +396,8 @@ def feedback_create(request):
|
||||
except Exception as e:
|
||||
messages.error(request, f"Error creating suggestion: {str(e)}")
|
||||
|
||||
hospitals = Hospital.objects.filter(status="active").order_by("name")
|
||||
context = {
|
||||
"hospitals": hospitals,
|
||||
"selected_hospital_id": str(hospital.id) if hospital else "",
|
||||
"is_create": True,
|
||||
"communication_request": communication_request,
|
||||
"prefill": prefill,
|
||||
@ -418,7 +413,7 @@ def feedback_update(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to edit this suggestion.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
@ -473,6 +468,14 @@ def feedback_update(request, pk):
|
||||
"feedback": feedback,
|
||||
"patients": patients,
|
||||
"is_create": False,
|
||||
"prefill": {
|
||||
"contact_name": feedback.contact_name or "",
|
||||
"contact_phone": feedback.contact_phone or "",
|
||||
"contact_email": feedback.contact_email or "",
|
||||
"message": feedback.message or "",
|
||||
"title": feedback.title or "",
|
||||
},
|
||||
"selected_hospital_id": feedback.hospital_id or "",
|
||||
}
|
||||
|
||||
return render(request, "feedback/feedback_form.html", context)
|
||||
@ -486,7 +489,7 @@ def feedback_delete(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to delete this suggestion.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
@ -525,7 +528,7 @@ def comment_import_list(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import CommentImport
|
||||
@ -547,7 +550,7 @@ def comment_list(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import PatientComment
|
||||
@ -616,7 +619,7 @@ def action_plan_list(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import CommentActionPlan
|
||||
@ -653,7 +656,7 @@ def export_comments_step1(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import PatientComment
|
||||
@ -682,7 +685,7 @@ def export_comments_step2(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import PatientComment
|
||||
@ -708,7 +711,7 @@ def export_action_plans(request):
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin()):
|
||||
if not (request.user.is_px_admin() or request.user.is_hospital_admin() or request.user.is_px_employee()):
|
||||
raise PermissionDenied
|
||||
|
||||
from .models import CommentActionPlan
|
||||
@ -723,6 +726,53 @@ def export_action_plans(request):
|
||||
return export_action_plans(qs)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def feedback_activate(request, pk):
|
||||
"""Activate a suggestion and auto-assign to the activating user."""
|
||||
feedback = get_object_or_404(Feedback, pk=pk, is_deleted=False)
|
||||
|
||||
user = request.user
|
||||
if not (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
):
|
||||
messages.error(request, _("You don't have permission to activate this suggestion."))
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
if feedback.activated_at:
|
||||
messages.warning(request, _("This suggestion has already been activated."))
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
feedback.activated_at = timezone.now()
|
||||
feedback.activated_by = user
|
||||
feedback.assigned_to = user
|
||||
feedback.assigned_at = timezone.now()
|
||||
if feedback.status == FeedbackStatus.SUBMITTED:
|
||||
feedback.status = FeedbackStatus.REVIEWED
|
||||
feedback.save()
|
||||
|
||||
FeedbackResponse.objects.create(
|
||||
feedback=feedback,
|
||||
response_type="note",
|
||||
message=f"Suggestion activated and assigned to {user.get_full_name()}",
|
||||
created_by=user,
|
||||
is_internal=True,
|
||||
)
|
||||
|
||||
AuditService.log_event(
|
||||
event_type="suggestion_activated",
|
||||
description=f"Suggestion '{feedback.title}' activated by {user.get_full_name()}",
|
||||
user=user,
|
||||
content_object=feedback,
|
||||
)
|
||||
|
||||
messages.success(request, _("Suggestion activated successfully."))
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@require_http_methods(["POST"])
|
||||
def feedback_assign(request, pk):
|
||||
@ -887,7 +937,7 @@ def feedback_add_response(request, pk):
|
||||
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_management()
|
||||
or user.is_department_manager()):
|
||||
or user.is_department_manager() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to add responses to this suggestion.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
@ -920,7 +970,7 @@ def feedback_toggle_featured(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to feature this suggestion.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
@ -941,7 +991,7 @@ def feedback_toggle_follow_up(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to modify this suggestion.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
@ -1070,7 +1120,7 @@ def feedback_create_action(request, pk):
|
||||
feedback = get_object_or_404(Feedback, pk=pk, is_deleted=False)
|
||||
|
||||
user = request.user
|
||||
if not (user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to create actions from suggestions.")
|
||||
return redirect("feedback:feedback_detail", pk=pk)
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ from apps.complaints.models import Complaint, Inquiry
|
||||
from apps.observations.models import Observation
|
||||
from apps.appreciation.models import Appreciation, AppreciationStatus, AppreciationVisibility
|
||||
from apps.feedback.models import Feedback, FeedbackType, FeedbackStatus, FeedbackCategory
|
||||
from apps.physicians.models import PhysicianIndividualRating
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -36,7 +37,7 @@ class ExternalComplaintCreateSerializer(serializers.Serializer):
|
||||
# Hospital context
|
||||
hospital = serializers.CharField(max_length=200, required=True)
|
||||
|
||||
# Location hierarchy (all optional)
|
||||
# Location hierarchy
|
||||
location_type = serializers.ChoiceField(
|
||||
choices=[("OP", "Outpatient"), ("IP", "Inpatient"), ("ER", "Emergency"), ("GENERAL", "General")],
|
||||
required=False,
|
||||
@ -44,7 +45,7 @@ class ExternalComplaintCreateSerializer(serializers.Serializer):
|
||||
default="",
|
||||
)
|
||||
area = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
department = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
department_code = serializers.CharField(max_length=200, required=True)
|
||||
section = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
|
||||
# Complaint content
|
||||
@ -72,17 +73,19 @@ class ExternalComplaintCreateSerializer(serializers.Serializer):
|
||||
except Area.DoesNotExist:
|
||||
raise serializers.ValidationError({"area": f"Area '{area_name}' not found for this hospital."})
|
||||
|
||||
dept_name = data.get("department")
|
||||
if dept_name:
|
||||
department_code = data.get("department_code")
|
||||
if department_code:
|
||||
from apps.organizations.models import Department
|
||||
try:
|
||||
data["department"] = Department.objects.get(
|
||||
name__iexact=dept_name.strip(), hospital=hospital, status="active"
|
||||
code__iexact=department_code.strip(), hospital=hospital, status="active"
|
||||
)
|
||||
except Department.DoesNotExist:
|
||||
raise serializers.ValidationError(
|
||||
{"department": f"Department '{dept_name}' not found for this hospital."}
|
||||
{"department_code": f"Department with code '{department_code}' not found for this hospital."}
|
||||
)
|
||||
else:
|
||||
data["department"] = None
|
||||
|
||||
section_name = data.get("section")
|
||||
if section_name:
|
||||
@ -156,7 +159,7 @@ class ExternalInquiryCreateSerializer(serializers.Serializer):
|
||||
default="",
|
||||
)
|
||||
area = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
department = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
department_code = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
section = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
|
||||
# Inquiry content
|
||||
@ -194,17 +197,19 @@ class ExternalInquiryCreateSerializer(serializers.Serializer):
|
||||
except Area.DoesNotExist:
|
||||
raise serializers.ValidationError({"area": f"Area '{area_name}' not found for this hospital."})
|
||||
|
||||
dept_name = data.get("department")
|
||||
if dept_name:
|
||||
department_code = data.get("department_code")
|
||||
if department_code:
|
||||
from apps.organizations.models import Department
|
||||
try:
|
||||
data["department"] = Department.objects.get(
|
||||
name__iexact=dept_name.strip(), hospital=hospital, status="active"
|
||||
code__iexact=department_code.strip(), hospital=hospital, status="active"
|
||||
)
|
||||
except Department.DoesNotExist:
|
||||
raise serializers.ValidationError(
|
||||
{"department": f"Department '{dept_name}' not found for this hospital."}
|
||||
{"department_code": f"Department with code '{department_code}' not found for this hospital."}
|
||||
)
|
||||
else:
|
||||
data["department"] = None
|
||||
|
||||
section_name = data.get("section")
|
||||
if section_name:
|
||||
@ -467,7 +472,7 @@ class ExternalComplaintSatisfactionSerializer(serializers.Serializer):
|
||||
|
||||
class ExternalDoctorRatingCreateSerializer(serializers.Serializer):
|
||||
|
||||
hospital_id = serializers.IntegerField(required=True)
|
||||
hospital = serializers.CharField(max_length=200, required=True)
|
||||
doctor_id = serializers.CharField(max_length=50, required=True)
|
||||
doctor_name = serializers.CharField(max_length=300, required=False, allow_blank=True, default="")
|
||||
rating = serializers.IntegerField(min_value=1, max_value=5, required=True)
|
||||
@ -481,15 +486,15 @@ class ExternalDoctorRatingCreateSerializer(serializers.Serializer):
|
||||
allow_blank=True,
|
||||
default="",
|
||||
)
|
||||
department_name = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
department_code = serializers.CharField(max_length=200, required=False, allow_blank=True, default="")
|
||||
admit_date = serializers.DateField(required=False, allow_null=True, default=None)
|
||||
discharge_date = serializers.DateField(required=False, allow_null=True, default=None)
|
||||
|
||||
def validate_hospital_id(self, value):
|
||||
def validate_hospital(self, value):
|
||||
from apps.organizations.models import Hospital
|
||||
|
||||
try:
|
||||
return Hospital.objects.get(id=value)
|
||||
return Hospital.objects.get(name__iexact=value.strip())
|
||||
except Hospital.DoesNotExist:
|
||||
raise serializers.ValidationError("Hospital not found.")
|
||||
|
||||
@ -497,21 +502,105 @@ class ExternalDoctorRatingCreateSerializer(serializers.Serializer):
|
||||
return value.strip()
|
||||
|
||||
def validate(self, data):
|
||||
from apps.organizations.models import Staff
|
||||
from apps.organizations.models import Department, Staff
|
||||
from apps.physicians.adapter import DoctorRatingAdapter
|
||||
|
||||
hospital = data.get("hospital_id")
|
||||
hospital = data.get("hospital")
|
||||
doctor_id = data.get("doctor_id", "").strip()
|
||||
|
||||
if hospital and doctor_id:
|
||||
staff = Staff.objects.filter(
|
||||
hospital=hospital, employee_id=doctor_id
|
||||
# Resolve department code → human-readable name (the rating model stores
|
||||
# a text department_name; HIS sends a stable department_code instead).
|
||||
department_code = data.get("department_code", "").strip()
|
||||
data["department_name"] = ""
|
||||
dept = None
|
||||
if department_code and hospital:
|
||||
dept = Department.objects.filter(
|
||||
code__iexact=department_code, hospital=hospital, status="active"
|
||||
).first()
|
||||
if not staff:
|
||||
if not dept:
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"doctor_id": f"Doctor with employee ID '{doctor_id}' not found at this hospital."
|
||||
}
|
||||
{"department_code": f"Department with code '{department_code}' not found for this hospital."}
|
||||
)
|
||||
data["department_name"] = dept.name_en or dept.name
|
||||
|
||||
if hospital and doctor_id:
|
||||
doctor_name = (data.get("doctor_name") or "").strip()
|
||||
|
||||
# 1. Canonical multi-strategy lookup (employee_id → license_number → name).
|
||||
staff = DoctorRatingAdapter.find_staff_by_doctor_id(
|
||||
doctor_id=doctor_id, hospital=hospital, doctor_name=doctor_name
|
||||
)
|
||||
|
||||
# 2. Auto-create the Staff if not found and we have a name to populate
|
||||
# the required first_name/last_name fields.
|
||||
if not staff:
|
||||
if not doctor_name:
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"doctor_name": (
|
||||
"Staff not found for this doctor_id; provide doctor_name "
|
||||
"to auto-create the staff record."
|
||||
)
|
||||
}
|
||||
)
|
||||
name_parts = doctor_name.split()
|
||||
first_name = name_parts[0]
|
||||
last_name = name_parts[-1] if len(name_parts) > 1 else name_parts[0]
|
||||
staff, created = Staff.objects.get_or_create(
|
||||
employee_id=doctor_id,
|
||||
defaults={
|
||||
"hospital": hospital,
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
"name": doctor_name,
|
||||
"staff_type": Staff.StaffType.PHYSICIAN,
|
||||
"job_title": "Physician",
|
||||
"physician": True,
|
||||
"status": "active",
|
||||
"department": dept,
|
||||
},
|
||||
)
|
||||
data["_staff_created"] = created
|
||||
|
||||
data["_staff"] = staff
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class ExternalDoctorRatingRetrieveSerializer(serializers.ModelSerializer):
|
||||
"""Read-only serializer for retrieving a doctor rating."""
|
||||
|
||||
hospital_name = serializers.CharField(source="hospital.name", read_only=True)
|
||||
staff_id = serializers.CharField(read_only=True, default=None)
|
||||
doctor_name_display = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = PhysicianIndividualRating
|
||||
fields = [
|
||||
"id",
|
||||
"staff_id",
|
||||
"hospital_name",
|
||||
"source",
|
||||
"source_reference",
|
||||
"doctor_id",
|
||||
"doctor_name",
|
||||
"doctor_name_raw",
|
||||
"doctor_name_display",
|
||||
"department_name",
|
||||
"patient_uhid",
|
||||
"patient_name",
|
||||
"patient_type",
|
||||
"admit_date",
|
||||
"discharge_date",
|
||||
"rating",
|
||||
"feedback",
|
||||
"rating_date",
|
||||
"is_aggregated",
|
||||
"aggregated_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
read_only_fields = fields
|
||||
|
||||
def get_doctor_name_display(self, obj):
|
||||
return obj.doctor_name or obj.doctor_name_raw or ""
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
"""
|
||||
External API views for complaints, inquiries, observations, appreciations, and suggestions.
|
||||
External API views for complaints, inquiries, observations, appreciations, suggestions, and doctor ratings.
|
||||
|
||||
All endpoints require X-API-Key header authentication.
|
||||
"""
|
||||
@ -24,6 +24,7 @@ from .api_serializers import (
|
||||
ExternalComplaintRetrieveSerializer,
|
||||
ExternalComplaintSatisfactionSerializer,
|
||||
ExternalDoctorRatingCreateSerializer,
|
||||
ExternalDoctorRatingRetrieveSerializer,
|
||||
ExternalInquiryCreateSerializer,
|
||||
ExternalInquiryRetrieveSerializer,
|
||||
ExternalObservationCreateSerializer,
|
||||
@ -161,8 +162,8 @@ class ExternalComplaintCreateView(ExternalAPIBase, APIView):
|
||||
patient=None,
|
||||
hospital=data["hospital"],
|
||||
department=data.get("department"),
|
||||
section=data.get("section"),
|
||||
area=data.get("area"),
|
||||
section=data.get("section") or None,
|
||||
area=data.get("area") or None,
|
||||
location_type=data.get("location_type", ""),
|
||||
title=data["title"],
|
||||
description=data["description"],
|
||||
@ -355,9 +356,9 @@ class ExternalInquiryCreateView(ExternalAPIBase, APIView):
|
||||
inquiry = Inquiry.objects.create(
|
||||
patient=None,
|
||||
hospital=data["hospital"],
|
||||
department=data.get("department"),
|
||||
section=data.get("section"),
|
||||
area=data.get("area"),
|
||||
department=data.get("department") or None,
|
||||
section=data.get("section") or None,
|
||||
area=data.get("area") or None,
|
||||
location_type=data.get("location_type", ""),
|
||||
subject=data["subject"],
|
||||
message=data["message"],
|
||||
@ -955,9 +956,9 @@ class ExternalDepartmentsListView(ExternalAPIBase, APIView):
|
||||
|
||||
class ExternalSectionsListView(ExternalAPIBase, APIView):
|
||||
"""
|
||||
GET /api/v1/external/sections/?hospital=<name>&department=<name>
|
||||
GET /api/v1/external/sections/?hospital=<name>&department_code=<code>
|
||||
|
||||
List sections for a department.
|
||||
List sections for a department (identified by its stable HIS department code).
|
||||
"""
|
||||
|
||||
entity_name = "lookup"
|
||||
@ -966,11 +967,11 @@ class ExternalSectionsListView(ExternalAPIBase, APIView):
|
||||
from apps.organizations.models import Department, Hospital, Section
|
||||
|
||||
hospital_name = request.query_params.get("hospital", "").strip()
|
||||
department_name = request.query_params.get("department", "").strip()
|
||||
department_code = request.query_params.get("department_code", "").strip()
|
||||
|
||||
if not hospital_name or not department_name:
|
||||
if not hospital_name or not department_code:
|
||||
return Response(
|
||||
{"detail": "hospital and department query parameters are required."},
|
||||
{"detail": "hospital and department_code query parameters are required."},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
@ -981,10 +982,13 @@ class ExternalSectionsListView(ExternalAPIBase, APIView):
|
||||
|
||||
try:
|
||||
department = Department.objects.get(
|
||||
name__iexact=department_name, hospital=hospital, status="active"
|
||||
code__iexact=department_code, hospital=hospital, status="active"
|
||||
)
|
||||
except Department.DoesNotExist:
|
||||
return Response({"detail": "Department not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response(
|
||||
{"detail": f"Department with code '{department_code}' not found for this hospital."},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
qs = Section.objects.filter(department=department, status="active").order_by("name_en")
|
||||
|
||||
@ -1023,8 +1027,9 @@ class ExternalDoctorRatingCreateView(ExternalAPIBase, APIView):
|
||||
ser.is_valid(raise_exception=True)
|
||||
data = ser.validated_data
|
||||
|
||||
hospital = data["hospital_id"]
|
||||
hospital = data["hospital"]
|
||||
staff = data.pop("_staff")
|
||||
staff_created = data.pop("_staff_created", False)
|
||||
|
||||
api_key = getattr(request, "api_key", None)
|
||||
if api_key and api_key.hospital and hospital != api_key.hospital:
|
||||
@ -1068,6 +1073,23 @@ class ExternalDoctorRatingCreateView(ExternalAPIBase, APIView):
|
||||
staff.physician = True
|
||||
staff.save(update_fields=["physician"])
|
||||
|
||||
if staff_created:
|
||||
AuditService.log_event(
|
||||
event_type="external_staff_auto_created",
|
||||
description=(
|
||||
f"Staff auto-created via external API: {staff.get_full_name()} "
|
||||
f"(employee_id={staff.employee_id})"
|
||||
),
|
||||
content_object=staff,
|
||||
metadata={
|
||||
"staff_id": str(staff.id),
|
||||
"employee_id": staff.employee_id,
|
||||
"doctor_id": data["doctor_id"],
|
||||
"hospital_id": str(hospital.id),
|
||||
"source": "external_api",
|
||||
},
|
||||
)
|
||||
|
||||
AuditService.log_event(
|
||||
event_type="external_doctor_rating_created",
|
||||
description=f"Doctor rating created via external API: {staff.get_full_name()} - {data['rating']}/5",
|
||||
@ -1085,9 +1107,98 @@ class ExternalDoctorRatingCreateView(ExternalAPIBase, APIView):
|
||||
{
|
||||
"success": True,
|
||||
"rating_id": str(rating.id),
|
||||
"staff_id": str(staff.id),
|
||||
"doctor_name": staff.get_full_name(),
|
||||
"rating": data["rating"],
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class ExternalDoctorRatingRetrieveView(ExternalAPIBase, APIView):
|
||||
"""
|
||||
GET /api/v1/external/doctor-ratings/list/
|
||||
GET /api/v1/external/doctor-ratings/<rating_id>/
|
||||
|
||||
List doctor ratings (scoped to the API key's hospital) or retrieve a
|
||||
single rating by its UUID.
|
||||
|
||||
List filters (all optional, in addition to hospital scoping):
|
||||
- doctor_id HIS employee ID (exact match)
|
||||
- rating Integer 1-5 (exact match)
|
||||
- created_from/created_to Created-at date range
|
||||
- updated_from/updated_to Updated-at date range
|
||||
- rating_from/rating_to Rating-date range (`rating_date` column)
|
||||
- page, page_size Pagination (default 20, max 100)
|
||||
"""
|
||||
|
||||
entity_name = "doctor_ratings"
|
||||
|
||||
def get(self, request, rating_id=None):
|
||||
access_err = self.check_entity_access()
|
||||
if access_err:
|
||||
return access_err
|
||||
|
||||
if rating_id:
|
||||
return self._retrieve(request, rating_id)
|
||||
return self._list(request)
|
||||
|
||||
def _retrieve(self, request, rating_id):
|
||||
from apps.physicians.models import PhysicianIndividualRating
|
||||
|
||||
qs = PhysicianIndividualRating.objects.filter(id=rating_id)
|
||||
qs = self.filter_queryset_by_hospital(qs)
|
||||
rating = qs.first()
|
||||
if not rating:
|
||||
return Response({"detail": "Doctor rating not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
ser = ExternalDoctorRatingRetrieveSerializer(rating)
|
||||
return Response(ser.data)
|
||||
|
||||
def _list(self, request):
|
||||
from apps.physicians.models import PhysicianIndividualRating
|
||||
|
||||
qs = PhysicianIndividualRating.objects.all()
|
||||
qs = self.filter_queryset_by_hospital(qs)
|
||||
qs = self.apply_list_filters(qs, request)
|
||||
|
||||
# Doctor-rating-specific filters
|
||||
doctor_id = request.query_params.get("doctor_id", "").strip()
|
||||
if doctor_id:
|
||||
qs = qs.filter(doctor_id=doctor_id)
|
||||
|
||||
rating_value = request.query_params.get("rating", "").strip()
|
||||
if rating_value:
|
||||
try:
|
||||
qs = qs.filter(rating=int(rating_value))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
rating_from = request.query_params.get("rating_from", "").strip()
|
||||
if rating_from:
|
||||
try:
|
||||
qs = qs.filter(rating_date__gte=self._parse_date(rating_from, start=True))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
rating_to = request.query_params.get("rating_to", "").strip()
|
||||
if rating_to:
|
||||
try:
|
||||
qs = qs.filter(rating_date__lte=self._parse_date(rating_to, start=False))
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
page_size = min(int(request.query_params.get("page_size", 20)), 100)
|
||||
page = int(request.query_params.get("page", 1))
|
||||
offset = (page - 1) * page_size
|
||||
total = qs.count()
|
||||
|
||||
items = qs[offset : offset + page_size]
|
||||
ser = ExternalDoctorRatingRetrieveSerializer(items, many=True)
|
||||
|
||||
return Response(
|
||||
{
|
||||
"count": total,
|
||||
"page": page,
|
||||
"page_size": page_size,
|
||||
"results": ser.data,
|
||||
}
|
||||
)
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-19 08:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('integrations', '0003_external_api_key'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='externalapikey',
|
||||
name='allowed_entities',
|
||||
field=models.JSONField(blank=True, default=list, help_text='List of entities this key can access: ["complaints","inquiries","observations","appreciations","suggestions","doctor_ratings"]'),
|
||||
),
|
||||
]
|
||||
@ -59,7 +59,7 @@ class ExternalAPIKey(UUIDModel, TimeStampedModel):
|
||||
allowed_entities = models.JSONField(
|
||||
default=list,
|
||||
blank=True,
|
||||
help_text='List of entities this key can access: ["complaints","inquiries","observations","appreciations","suggestions"]',
|
||||
help_text='List of entities this key can access: ["complaints","inquiries","observations","appreciations","suggestions","doctor_ratings"]',
|
||||
)
|
||||
|
||||
# Rate limiting
|
||||
|
||||
@ -14,6 +14,7 @@ from .api_views import (
|
||||
ExternalComplaintSatisfactionView,
|
||||
ExternalDepartmentsListView,
|
||||
ExternalDoctorRatingCreateView,
|
||||
ExternalDoctorRatingRetrieveView,
|
||||
ExternalHospitalsListView,
|
||||
ExternalInquiryCreateView,
|
||||
ExternalInquiryRetrieveView,
|
||||
@ -145,4 +146,14 @@ urlpatterns = [
|
||||
ExternalDoctorRatingCreateView.as_view(),
|
||||
name="external-doctor-rating-create",
|
||||
),
|
||||
path(
|
||||
"doctor-ratings/list/",
|
||||
ExternalDoctorRatingRetrieveView.as_view(),
|
||||
name="external-doctor-rating-list",
|
||||
),
|
||||
path(
|
||||
"doctor-ratings/<uuid:rating_id>/",
|
||||
ExternalDoctorRatingRetrieveView.as_view(),
|
||||
name="external-doctor-rating-detail",
|
||||
),
|
||||
]
|
||||
|
||||
@ -48,12 +48,22 @@ def journey_instance_list(request):
|
||||
|
||||
# Apply RBAC filters
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
if user.is_px_admin():
|
||||
pass # See all
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_source_user():
|
||||
queryset = queryset.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
else:
|
||||
@ -185,8 +195,22 @@ def journey_template_list(request):
|
||||
|
||||
# Apply RBAC filters
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
if user.is_px_admin():
|
||||
pass # See all
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
queryset = queryset.filter(department=user.department)
|
||||
elif user.is_source_user():
|
||||
queryset = queryset.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
queryset = queryset.filter(hospital=user.hospital)
|
||||
else:
|
||||
|
||||
@ -23,17 +23,8 @@ def get_email_header_html():
|
||||
logo_url = getattr(settings, "EMAIL_LOGO_URL", f"{settings.STATIC_URL}img/HH_P_V_Logo(hospital)_.png")
|
||||
if not logo_url.startswith("http"):
|
||||
logo_url = f"https://{settings.ALLOWED_HOSTS[0]}{logo_url}" if getattr(settings, "ALLOWED_HOSTS", []) else logo_url
|
||||
return f"""<div style="padding: 20px 30px; background-color: #ffffff;">
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td style="text-align: left; width: 90px; vertical-align: middle;">
|
||||
<img src="{logo_url}" alt="Al Hammadi" width="75" style="display: block; max-width: 75px; height: auto;">
|
||||
</td>
|
||||
<td style="text-align: right; direction: rtl; font-size: 15px; color: #005696; font-weight: 700; vertical-align: middle; font-family: 'Segoe UI', Tahoma, sans-serif;">
|
||||
مستشفى الحمادي
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
return f"""<div style="padding: 20px 30px; background-color: #ffffff;" align="center">
|
||||
<img src="{logo_url}" alt="Al Hammadi" width="240" style="display: block; margin: 0 auto; max-width: 240px; height: auto;">
|
||||
</div>
|
||||
<div style="height: 2px; background-color: #005696; font-size: 1px; line-height: 1px;"> </div>"""
|
||||
|
||||
@ -295,6 +286,8 @@ class NotificationService:
|
||||
metadata=None,
|
||||
notification_type="system",
|
||||
user=None,
|
||||
attachments=None,
|
||||
cc=None,
|
||||
):
|
||||
"""
|
||||
Send Email notification and create corresponding in-app notification.
|
||||
@ -308,6 +301,14 @@ class NotificationService:
|
||||
metadata: Additional metadata dict (optional)
|
||||
notification_type: Type of notification (default: 'system')
|
||||
user: User instance to create in-app notification for (optional, will try to lookup by email)
|
||||
attachments: Optional list of (filename, content_bytes, mimetype) tuples.
|
||||
Only applied on the standard Django email backend path; silently
|
||||
skipped on the external Email API path (which would need its own
|
||||
upload mechanism).
|
||||
cc: Optional list of email addresses to CC. Only applied on the
|
||||
standard Django email backend path; silently skipped on the
|
||||
external Email API path. In-app notifications are created for
|
||||
each CC'd user whose email maps to a User account.
|
||||
|
||||
Returns:
|
||||
NotificationLog instance
|
||||
@ -326,16 +327,34 @@ class NotificationService:
|
||||
|
||||
from apps.accounts.models import User
|
||||
|
||||
# Normalise CC list to a clean list of strings (drop None/empty)
|
||||
cc_list = [addr for addr in (cc or []) if addr]
|
||||
|
||||
# Enrich metadata with CC trace
|
||||
log_metadata = dict(metadata or {})
|
||||
if cc_list:
|
||||
log_metadata = {**log_metadata, "cc": cc_list}
|
||||
|
||||
# Check if Email API is enabled and use it (simulator or external API)
|
||||
email_api_config = settings.EXTERNAL_NOTIFICATION_API.get("email", {})
|
||||
if email_api_config.get("enabled", False):
|
||||
skipped = []
|
||||
if attachments:
|
||||
skipped.append(f"{len(attachments)} attachment(s)")
|
||||
if cc_list:
|
||||
skipped.append(f"{len(cc_list)} CC recipient(s)")
|
||||
if skipped:
|
||||
logger.warning(
|
||||
f"Email API path does not support {' or '.join(skipped)}; "
|
||||
f"sending email to {email} without them."
|
||||
)
|
||||
log = NotificationService.send_email_via_api(
|
||||
message=message,
|
||||
email=email,
|
||||
subject=subject,
|
||||
html_message=html_message,
|
||||
related_object=related_object,
|
||||
metadata=metadata,
|
||||
metadata=log_metadata,
|
||||
)
|
||||
# Create in-app notification after sending via API
|
||||
NotificationService._create_in_app_notification_from_email(
|
||||
@ -351,31 +370,61 @@ class NotificationService:
|
||||
message=message,
|
||||
content_object=related_object,
|
||||
provider="console", # TODO: Replace with actual provider
|
||||
metadata=metadata or {},
|
||||
metadata=log_metadata,
|
||||
)
|
||||
|
||||
# Create in-app notification
|
||||
# Create in-app notification for the primary (To:) recipient
|
||||
NotificationService._create_in_app_notification_from_email(
|
||||
log, email, subject, message, notification_type, related_object, user
|
||||
)
|
||||
|
||||
# Create in-app notifications for each CC'd user (silently skips emails
|
||||
# that don't map to a User account).
|
||||
for cc_addr in cc_list:
|
||||
if cc_addr.lower() == email.lower():
|
||||
continue # Already covered by the To: notification
|
||||
NotificationService._create_in_app_notification_from_email(
|
||||
log, cc_addr, subject, message, notification_type, related_object, None
|
||||
)
|
||||
|
||||
# Check if Email is enabled
|
||||
email_config = settings.NOTIFICATION_CHANNELS.get("email", {})
|
||||
if not email_config.get("enabled", True):
|
||||
logger.info(f"[Email Console] To: {email} | Subject: {subject} | Message: {message}")
|
||||
cc_summary = f" | CC: {', '.join(cc_list)}" if cc_list else ""
|
||||
logger.info(f"[Email Console] To: {email}{cc_summary} | Subject: {subject} | Message: {message}")
|
||||
log.mark_sent()
|
||||
return log
|
||||
|
||||
# Send email using Django's email backend
|
||||
try:
|
||||
send_mail(
|
||||
subject=subject,
|
||||
message=message,
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
recipient_list=[email],
|
||||
html_message=html_message,
|
||||
fail_silently=False,
|
||||
)
|
||||
if attachments or cc_list:
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
|
||||
msg = EmailMultiAlternatives(
|
||||
subject=subject,
|
||||
body=message,
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
to=[email],
|
||||
cc=cc_list if cc_list else None,
|
||||
)
|
||||
if html_message:
|
||||
msg.attach_alternative(html_message, "text/html")
|
||||
for att in (attachments or []):
|
||||
# att is (filename, content_bytes, mimetype)
|
||||
try:
|
||||
msg.attach(*att)
|
||||
except Exception:
|
||||
logger.exception(f"Failed to attach {att[0] if att else '?'} to email for {email}")
|
||||
msg.send(fail_silently=False)
|
||||
else:
|
||||
send_mail(
|
||||
subject=subject,
|
||||
message=message,
|
||||
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||
recipient_list=[email],
|
||||
html_message=html_message,
|
||||
fail_silently=False,
|
||||
)
|
||||
log.mark_sent()
|
||||
logger.info(f"Email sent to {email}: {subject}")
|
||||
except Exception as e:
|
||||
|
||||
@ -8,6 +8,7 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.accounts.models import User
|
||||
from apps.core.validators import SAUDI_PHONE_HTML_PATTERN, validate_saudi_phone
|
||||
from apps.organizations.models import Area, Department, Hospital, LocationType
|
||||
from apps.organizations.models import Section as OrgSection
|
||||
|
||||
@ -77,6 +78,7 @@ class ObservationPublicForm(forms.ModelForm):
|
||||
"reporter_name",
|
||||
"reporter_phone",
|
||||
"reporter_email",
|
||||
"national_id",
|
||||
]
|
||||
widgets = {
|
||||
"location_type": forms.Select(
|
||||
@ -139,6 +141,12 @@ class ObservationPublicForm(forms.ModelForm):
|
||||
"placeholder": "Your email (optional)",
|
||||
}
|
||||
),
|
||||
"national_id": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "National ID/Iqama (optional)",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -198,11 +206,15 @@ class ObservationInternalForm(forms.ModelForm):
|
||||
"location_type",
|
||||
"area",
|
||||
"description",
|
||||
"title",
|
||||
"location_text",
|
||||
"section",
|
||||
"incident_datetime",
|
||||
"patient_file_number",
|
||||
"patient_name",
|
||||
"patient_national_id",
|
||||
"patient_phone",
|
||||
"patient_email",
|
||||
"assigned_department",
|
||||
"assigned_to",
|
||||
"px_source",
|
||||
]
|
||||
widgets = {
|
||||
@ -225,16 +237,46 @@ class ObservationInternalForm(forms.ModelForm):
|
||||
"placeholder": "Please describe what you observed in detail...",
|
||||
}
|
||||
),
|
||||
"title": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Brief title (optional)",
|
||||
}
|
||||
),
|
||||
"location_text": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Specific location details (e.g. room number, floor)",
|
||||
}
|
||||
),
|
||||
"incident_datetime": forms.DateTimeInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"type": "datetime-local",
|
||||
}
|
||||
),
|
||||
"patient_file_number": forms.TextInput(
|
||||
"patient_name": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Patient MRN / file number",
|
||||
"placeholder": "Patient name (optional)",
|
||||
}
|
||||
),
|
||||
"patient_national_id": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "National ID/Iqama (optional)",
|
||||
}
|
||||
),
|
||||
"patient_phone": forms.TextInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Patient phone (optional)",
|
||||
}
|
||||
),
|
||||
"patient_email": forms.EmailInput(
|
||||
attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": "Patient email (optional)",
|
||||
}
|
||||
),
|
||||
"assigned_department": forms.Select(
|
||||
@ -242,11 +284,6 @@ class ObservationInternalForm(forms.ModelForm):
|
||||
"class": "form-select",
|
||||
}
|
||||
),
|
||||
"assigned_to": forms.Select(
|
||||
attrs={
|
||||
"class": "form-select",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
section = forms.ModelChoiceField(
|
||||
@ -311,15 +348,6 @@ class ObservationInternalForm(forms.ModelForm):
|
||||
self.fields["assigned_department"].queryset = Department.objects.filter(status="active").order_by("name_en")
|
||||
self.fields["assigned_department"].empty_label = "Select department (optional)"
|
||||
|
||||
if hospital:
|
||||
from apps.core.utils import get_assignable_users
|
||||
self.fields["assigned_to"].queryset = get_assignable_users(hospital)
|
||||
else:
|
||||
self.fields["assigned_to"].queryset = User.objects.filter(is_active=True).order_by(
|
||||
"first_name", "last_name"
|
||||
)
|
||||
self.fields["assigned_to"].empty_label = "Select assignee (optional)"
|
||||
|
||||
def clean_description(self):
|
||||
description = self.cleaned_data.get("description", "")
|
||||
if len(description.strip()) < 10:
|
||||
@ -725,7 +753,13 @@ class PublicObservationForm(forms.ModelForm):
|
||||
label=_("Mobile Number"),
|
||||
max_length=20,
|
||||
required=True,
|
||||
widget=forms.TextInput(attrs={"class": "form-control", "placeholder": _("Your mobile number")}),
|
||||
validators=[validate_saudi_phone],
|
||||
widget=forms.TextInput(attrs={
|
||||
"class": "form-control",
|
||||
"placeholder": _("05XXXXXXXX or +9665XXXXXXXX"),
|
||||
"pattern": SAUDI_PHONE_HTML_PATTERN,
|
||||
"inputmode": "tel",
|
||||
}),
|
||||
)
|
||||
reporter_email = forms.EmailField(
|
||||
label=_("Email Address"),
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-18 14:46
|
||||
|
||||
import apps.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('observations', '0020_remove_observation_dept_response_acceptance_notes_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='observation',
|
||||
name='reporter_phone',
|
||||
field=models.CharField(blank=True, help_text='Optional phone number for follow-up', max_length=20, validators=[apps.core.validators.validate_saudi_phone]),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-19 06:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('observations', '0021_alter_observation_reporter_phone'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='observation',
|
||||
name='national_id',
|
||||
field=models.CharField(blank=True, help_text='Optional National ID/Iqama of the reporter', max_length=20),
|
||||
),
|
||||
]
|
||||
33
apps/observations/migrations/0023_add_patient_info_fields.py
Normal file
33
apps/observations/migrations/0023_add_patient_info_fields.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Generated by Django 6.0.1 on 2026-07-19 08:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('observations', '0022_observation_add_national_id'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='observation',
|
||||
name='patient_email',
|
||||
field=models.EmailField(blank=True, help_text='Email of the patient', max_length=254),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='observation',
|
||||
name='patient_name',
|
||||
field=models.CharField(blank=True, help_text='Name of the patient involved', max_length=200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='observation',
|
||||
name='patient_national_id',
|
||||
field=models.CharField(blank=True, help_text='National ID/Iqama of the patient', max_length=20),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='observation',
|
||||
name='patient_phone',
|
||||
field=models.CharField(blank=True, help_text='Phone number of the patient', max_length=20),
|
||||
),
|
||||
]
|
||||
@ -20,6 +20,7 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from apps.core.models import SoftDeleteModel, TimeStampedModel, UUIDModel
|
||||
from apps.core.validators import validate_saudi_phone
|
||||
from apps.organizations.models import LocationType
|
||||
|
||||
|
||||
@ -363,13 +364,18 @@ class Observation(UUIDModel, TimeStampedModel, SoftDeleteModel):
|
||||
# Optional reporter information (anonymous supported)
|
||||
reporter_staff_id = models.CharField(max_length=50, blank=True, help_text="Optional staff ID of the reporter")
|
||||
reporter_name = models.CharField(max_length=200, blank=True, help_text="Optional name of the reporter")
|
||||
reporter_phone = models.CharField(max_length=20, blank=True, help_text="Optional phone number for follow-up")
|
||||
reporter_phone = models.CharField(max_length=20, blank=True, validators=[validate_saudi_phone], help_text="Optional phone number for follow-up")
|
||||
reporter_email = models.EmailField(blank=True, help_text="Optional email for follow-up")
|
||||
national_id = models.CharField(max_length=20, blank=True, help_text="Optional National ID/Iqama of the reporter")
|
||||
|
||||
# Patient / subject information
|
||||
patient_file_number = models.CharField(
|
||||
max_length=100, blank=True, help_text="Medical record number / file number of the patient"
|
||||
)
|
||||
patient_name = models.CharField(max_length=200, blank=True, help_text="Name of the patient involved")
|
||||
patient_national_id = models.CharField(max_length=20, blank=True, help_text="National ID/Iqama of the patient")
|
||||
patient_phone = models.CharField(max_length=20, blank=True, help_text="Phone number of the patient")
|
||||
patient_email = models.EmailField(blank=True, help_text="Email of the patient")
|
||||
|
||||
# Status and workflow
|
||||
status = models.CharField(
|
||||
|
||||
@ -55,6 +55,7 @@ class ObservationService:
|
||||
reporter_name: str = "",
|
||||
reporter_phone: str = "",
|
||||
reporter_email: str = "",
|
||||
national_id: str = "",
|
||||
client_ip: str = None,
|
||||
user_agent: str = "",
|
||||
attachments: list = None,
|
||||
@ -99,6 +100,7 @@ class ObservationService:
|
||||
reporter_name=reporter_name,
|
||||
reporter_phone=reporter_phone,
|
||||
reporter_email=reporter_email,
|
||||
national_id=national_id,
|
||||
client_ip=client_ip,
|
||||
user_agent=user_agent,
|
||||
hospital=hospital,
|
||||
|
||||
@ -627,3 +627,39 @@ def send_observation_dept_response_reminders():
|
||||
"first_reminder_count": first_reminder_count,
|
||||
"second_reminder_count": second_reminder_count,
|
||||
}
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_observation_reminder_email(observation_id, reminder_type, recipients):
|
||||
"""Send a reminder email to department champion and manager for an observation."""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from apps.observations.models import Observation
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
|
||||
observation = Observation.objects.get(pk=observation_id)
|
||||
|
||||
for recipient in recipients:
|
||||
NotificationService.send_email(
|
||||
email=recipient["email"],
|
||||
subject=f"Reminder: Observation {observation.tracking_code} - Response Required",
|
||||
message=f"This is a reminder that observation {observation.tracking_code} is awaiting your department's response. Please submit your response as soon as possible.",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Reminder: Response Required</h2>
|
||||
<p style="margin: 0 0 12px 0;">This is a reminder that observation <strong>{observation.tracking_code}</strong> is awaiting your department's response.</p>
|
||||
<p style="margin: 0; color: #6b7280;">Please submit your response as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=observation,
|
||||
)
|
||||
|
||||
logger.info(f"Observation {observation_id} {reminder_type} reminder sent to {len(recipients)} recipients")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send observation reminder email for {observation_id}: {e}")
|
||||
|
||||
@ -195,6 +195,7 @@ def observation_create_public(request):
|
||||
reporter_name=form.cleaned_data.get("reporter_name", ""),
|
||||
reporter_phone=form.cleaned_data.get("reporter_phone", ""),
|
||||
reporter_email=form.cleaned_data.get("reporter_email", ""),
|
||||
national_id=form.cleaned_data.get("national_id", ""),
|
||||
client_ip=client_ip,
|
||||
user_agent=user_agent,
|
||||
attachments=attachments,
|
||||
@ -316,8 +317,8 @@ def observation_create(request):
|
||||
description=form.cleaned_data["description"],
|
||||
severity=form.cleaned_data.get("severity") or request.POST.get("severity", "medium"),
|
||||
category=None,
|
||||
title="",
|
||||
location_text="",
|
||||
title=form.cleaned_data.get("title", "") or "",
|
||||
location_text=form.cleaned_data.get("location_text", "") or "",
|
||||
incident_datetime=form.cleaned_data.get("incident_datetime"),
|
||||
reporter_staff_id=user.employee_id or "",
|
||||
reporter_name=user.get_full_name(),
|
||||
@ -332,23 +333,28 @@ def observation_create(request):
|
||||
)
|
||||
|
||||
assigned_dept = form.cleaned_data.get("assigned_department")
|
||||
assigned_user = form.cleaned_data.get("assigned_to")
|
||||
section = form.cleaned_data.get("section")
|
||||
if assigned_dept:
|
||||
observation.assigned_department = assigned_dept
|
||||
if assigned_user:
|
||||
observation.assigned_to = assigned_user
|
||||
observation.status = ObservationStatus.IN_PROGRESS
|
||||
ObservationStatusLog.objects.create(
|
||||
observation=observation,
|
||||
from_status=ObservationStatus.OPEN,
|
||||
to_status=ObservationStatus.IN_PROGRESS,
|
||||
comment="Auto-assigned during creation",
|
||||
)
|
||||
if section:
|
||||
observation.section = section
|
||||
|
||||
observation.patient_name = form.cleaned_data.get("patient_name", "") or ""
|
||||
observation.patient_national_id = form.cleaned_data.get("patient_national_id", "") or ""
|
||||
observation.patient_phone = form.cleaned_data.get("patient_phone", "") or ""
|
||||
observation.patient_email = form.cleaned_data.get("patient_email", "") or ""
|
||||
|
||||
observation.assigned_to = user
|
||||
observation.status = ObservationStatus.IN_PROGRESS
|
||||
observation.save()
|
||||
|
||||
ObservationStatusLog.objects.create(
|
||||
observation=observation,
|
||||
from_status=ObservationStatus.OPEN,
|
||||
to_status=ObservationStatus.IN_PROGRESS,
|
||||
comment="Auto-assigned during creation",
|
||||
)
|
||||
|
||||
comm_req_id = request.POST.get("comm_req")
|
||||
if comm_req_id:
|
||||
try:
|
||||
@ -644,13 +650,13 @@ def observation_detail(request, pk):
|
||||
"status_form": status_form,
|
||||
"note_form": note_form,
|
||||
"status_choices": ObservationStatus.choices,
|
||||
"can_triage": user.has_perm("observations.triage_observation") or user.is_px_admin(),
|
||||
"can_triage": user.has_perm("observations.triage_observation") or user.is_px_admin() or user.is_px_employee(),
|
||||
"can_convert": user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee(),
|
||||
"can_send_to_department": user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee() or user.is_department_manager() or user.is_px_management(),
|
||||
"can_respond_to_department": user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee() or (user.is_champion() and observation.assigned_department == user.department),
|
||||
"can_send_reminder": user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee(),
|
||||
"can_delete": user.is_px_admin() or user.is_hospital_admin() or user.is_px_management() or user.is_px_employee(),
|
||||
"can_admin": user.is_px_admin() or user.is_hospital_admin(),
|
||||
"can_admin": user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee(),
|
||||
"linked_rcas": linked_rcas,
|
||||
}
|
||||
|
||||
@ -734,7 +740,7 @@ def observation_triage(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.has_perm("observations.triage_observation") or user.is_px_admin()):
|
||||
if not (user.has_perm("observations.triage_observation") or user.is_px_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to triage observations.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
|
||||
@ -770,7 +776,7 @@ def observation_change_status(request, pk):
|
||||
|
||||
# Check permission
|
||||
user = request.user
|
||||
if not (user.has_perm("observations.triage_observation") or user.is_px_admin() or user.is_hospital_admin()):
|
||||
if not (user.has_perm("observations.triage_observation") or user.is_px_admin() or user.is_hospital_admin() or user.is_px_employee()):
|
||||
messages.error(request, "You don't have permission to change observation status.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
|
||||
@ -1619,6 +1625,8 @@ def observation_department_response(request, pk):
|
||||
if not (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or user.is_px_management()
|
||||
or user.is_px_employee()
|
||||
or (user.is_champion() and observation.assigned_department == user.department)
|
||||
):
|
||||
messages.error(request, "You don't have permission to respond to this observation.")
|
||||
@ -1681,61 +1689,31 @@ Generate a JSON response with:
|
||||
is_internal=True,
|
||||
)
|
||||
|
||||
try:
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
from apps.core.utils import build_public_track_url
|
||||
|
||||
track_url = build_public_track_url("observation", observation.tracking_code)
|
||||
|
||||
if observation.reporter_phone:
|
||||
NotificationService.send_sms(
|
||||
phone=observation.reporter_phone,
|
||||
message=f"PX360: Your observation {observation.tracking_code} has been responded to. View details: {track_url}",
|
||||
related_object=observation,
|
||||
metadata={"notification_type": "observation_department_response"},
|
||||
)
|
||||
|
||||
if observation.reporter_email:
|
||||
email_subject = f"PX360: Response to Your Observation {observation.tracking_code}"
|
||||
email_body = (
|
||||
f"Dear Valued Reporter,\n\n"
|
||||
f"Your observation {observation.tracking_code} has been responded to.\n\n"
|
||||
f"To view the full response, please visit:\n{track_url}\n\n"
|
||||
f"Thank you for your contribution.\n\n"
|
||||
f"Tracking Code: {observation.tracking_code}\n"
|
||||
f"This is an automated message from PX 360."
|
||||
)
|
||||
NotificationService.send_email(
|
||||
email=observation.reporter_email,
|
||||
subject=email_subject,
|
||||
message=email_body,
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Response to Your Observation</h2>
|
||||
<p style="margin: 0 0 12px 0;">Dear Valued Reporter,</p>
|
||||
<p style="margin: 0 0 12px 0;">Your observation <strong>{observation.tracking_code}</strong> has been responded to.</p>
|
||||
<div style="text-align: center; margin: 20px 0;">
|
||||
<a href="{track_url}" style="background: #005696; color: white; padding: 10px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">View Response</a>
|
||||
</div>
|
||||
<p style="margin: 0 0 6px 0; color: #6b7280; font-size: 13px;">Tracking Code: {observation.tracking_code}</p>
|
||||
<p style="margin: 0 0 6px 0; color: #6b7280; font-size: 13px;">Thank you for your contribution.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=observation,
|
||||
metadata={"notification_type": "observation_department_response_email"},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to send observation response notification: {e}")
|
||||
|
||||
messages.success(request, "Department response submitted successfully.")
|
||||
# Prefer a user-supplied relative next URL (e.g. back to the department
|
||||
# observation detail page) over the default department landing page.
|
||||
# Block protocol-relative URLs (``//host/`` or ``/\\host/``) to prevent
|
||||
# open-redirect abuse.
|
||||
next_url = request.POST.get("next")
|
||||
if not (
|
||||
next_url
|
||||
and next_url.startswith("/")
|
||||
and not next_url.startswith("//")
|
||||
and not next_url.startswith("/\\")
|
||||
):
|
||||
next_url = None
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
from django.http import JsonResponse
|
||||
from django.urls import reverse
|
||||
redirect_url = reverse("organizations:department_detail", kwargs={"pk": user.department.pk}) if user.department else reverse("observations:observation_detail", kwargs={"pk": pk})
|
||||
if next_url:
|
||||
redirect_url = next_url
|
||||
elif user.department:
|
||||
redirect_url = reverse("organizations:department_detail", kwargs={"pk": user.department.pk})
|
||||
else:
|
||||
redirect_url = reverse("observations:observation_detail", kwargs={"pk": pk})
|
||||
return JsonResponse({"success": True, "redirect_url": redirect_url})
|
||||
if next_url:
|
||||
return redirect(next_url)
|
||||
if user.department:
|
||||
return redirect("organizations:department_detail", pk=user.department.pk)
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
@ -1771,30 +1749,30 @@ def observation_send_dept_response_reminder(request, pk):
|
||||
|
||||
reminder_type = request.POST.get("reminder_type", "first")
|
||||
|
||||
if reminder_type == "first" and observation.dept_response_reminder_sent_at:
|
||||
messages.warning(request, "First reminder already sent.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
if reminder_type == "second" and observation.dept_response_second_reminder_sent_at:
|
||||
messages.warning(request, "Second reminder already sent.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
if reminder_type == "second" and not observation.dept_response_reminder_sent_at:
|
||||
messages.warning(request, "Please send the first reminder before the second.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
|
||||
try:
|
||||
from apps.notifications.services import NotificationService, get_email_header_html
|
||||
from apps.organizations.department_contacts import get_champion_and_manager
|
||||
|
||||
targets = get_champion_and_manager(dept)
|
||||
if not targets:
|
||||
messages.error(request, f"No champion or manager assigned to {dept.name}.")
|
||||
return redirect("observations:observation_detail", pk=pk)
|
||||
|
||||
recipients = []
|
||||
if dept.champion and dept.champion.user and dept.champion.user.email:
|
||||
recipients.append(dept.champion.user)
|
||||
|
||||
for recipient in recipients:
|
||||
NotificationService.send_email(
|
||||
email=recipient.email,
|
||||
subject=f"Reminder: Observation {observation.tracking_code} - Response Required",
|
||||
message=f"This is a reminder that observation {observation.tracking_code} is awaiting your department's response. Please submit your response as soon as possible.",
|
||||
html_message=f"""
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden;">
|
||||
{get_email_header_html()}
|
||||
<div style="padding: 20px;">
|
||||
<h2 style="color: #005696; font-size: 18px; margin: 0 0 12px 0;">Reminder: Response Required</h2>
|
||||
<p style="margin: 0 0 12px 0;">This is a reminder that observation <strong>{observation.tracking_code}</strong> is awaiting your department's response.</p>
|
||||
<p style="margin: 0; color: #6b7280;">Please submit your response as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
""",
|
||||
related_object=observation,
|
||||
)
|
||||
for target in targets:
|
||||
email = target.get("email") or ""
|
||||
if email:
|
||||
recipients.append({"email": email, "name": target.get("display_name", "")})
|
||||
|
||||
if reminder_type == "first":
|
||||
observation.dept_response_reminder_sent_at = timezone.now()
|
||||
@ -1802,6 +1780,9 @@ def observation_send_dept_response_reminder(request, pk):
|
||||
observation.dept_response_second_reminder_sent_at = timezone.now()
|
||||
observation.save()
|
||||
|
||||
from apps.observations.tasks import send_observation_reminder_email
|
||||
send_observation_reminder_email.delay(str(observation.id), reminder_type, recipients)
|
||||
|
||||
ObservationNote.objects.create(
|
||||
observation=observation,
|
||||
note=f"Manual {reminder_type} reminder sent by {user.get_full_name()}",
|
||||
@ -1823,11 +1804,13 @@ def observation_send_dept_response_reminder(request, pk):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("observations.manage_categories", raise_exception=True)
|
||||
def category_list(request):
|
||||
"""
|
||||
List observation categories.
|
||||
"""
|
||||
if not (request.user.has_perm("observations.manage_categories") or request.user.is_px_admin() or request.user.is_px_employee()):
|
||||
from django.core.exceptions import PermissionDenied
|
||||
raise PermissionDenied
|
||||
categories = ObservationCategory.objects.all().order_by("sort_order", "name_en")
|
||||
|
||||
context = {
|
||||
@ -1838,12 +1821,14 @@ def category_list(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("observations.manage_categories", raise_exception=True)
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def category_create(request):
|
||||
"""
|
||||
Create new observation category.
|
||||
"""
|
||||
if not (request.user.has_perm("observations.manage_categories") or request.user.is_px_admin() or request.user.is_px_employee()):
|
||||
from django.core.exceptions import PermissionDenied
|
||||
raise PermissionDenied
|
||||
if request.method == "POST":
|
||||
form = ObservationCategoryForm(request.POST)
|
||||
if form.is_valid():
|
||||
@ -1862,12 +1847,14 @@ def category_create(request):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("observations.manage_categories", raise_exception=True)
|
||||
@require_http_methods(["GET", "POST"])
|
||||
def category_edit(request, pk):
|
||||
"""
|
||||
Edit observation category.
|
||||
"""
|
||||
if not (request.user.has_perm("observations.manage_categories") or request.user.is_px_admin() or request.user.is_px_employee()):
|
||||
from django.core.exceptions import PermissionDenied
|
||||
raise PermissionDenied
|
||||
category = get_object_or_404(ObservationCategory, pk=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
@ -1889,12 +1876,14 @@ def category_edit(request, pk):
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("observations.manage_categories", raise_exception=True)
|
||||
@require_http_methods(["POST"])
|
||||
def category_delete(request, pk):
|
||||
"""
|
||||
Delete observation category.
|
||||
"""
|
||||
if not (request.user.has_perm("observations.manage_categories") or request.user.is_px_admin() or request.user.is_px_employee()):
|
||||
from django.core.exceptions import PermissionDenied
|
||||
raise PermissionDenied
|
||||
category = get_object_or_404(ObservationCategory, pk=pk)
|
||||
|
||||
# Check if category is in use
|
||||
|
||||
59
apps/organizations/management/commands/export_sections.py
Normal file
59
apps/organizations/management/commands/export_sections.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
Management command to export sections (English name, Arabic name, department) to a CSV file.
|
||||
|
||||
Usage:
|
||||
python manage.py export_sections --output-dir ./exports
|
||||
"""
|
||||
import csv
|
||||
import os
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from apps.organizations.models import Section
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Export sections (English name, Arabic name, department) to a CSV file'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--output-dir',
|
||||
type=str,
|
||||
default='./exports',
|
||||
help='Directory to export the sections CSV file to',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
output_dir = options['output_dir']
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
filepath = os.path.join(output_dir, 'sections.csv')
|
||||
qs = (
|
||||
Section.objects.all()
|
||||
.select_related('department')
|
||||
.order_by('department__name_en', 'name_en')
|
||||
)
|
||||
|
||||
seen = set()
|
||||
rows = []
|
||||
for section in qs:
|
||||
dept = section.department
|
||||
row = (
|
||||
section.name_en or '',
|
||||
section.name_ar or '',
|
||||
dept.name_en if dept else '',
|
||||
dept.name_ar if dept else '',
|
||||
)
|
||||
if row in seen:
|
||||
continue
|
||||
seen.add(row)
|
||||
rows.append(row)
|
||||
|
||||
with open(filepath, 'w', newline='', encoding='utf-8-sig') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Name (EN)', 'Name (AR)', 'Department (EN)', 'Department (AR)'])
|
||||
writer.writerows(rows)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f'Exported {len(rows)} unique sections to {filepath}'
|
||||
))
|
||||
@ -1939,9 +1939,28 @@ def department_detail(request, pk):
|
||||
"involved_staff__staff"
|
||||
).distinct().order_by("-created_at")
|
||||
|
||||
inquiries = Inquiry.objects.filter(
|
||||
Q(department=department, sent_to_department=True) | Q(outgoing_department=department)
|
||||
).select_related("assigned_to", "patient").order_by("-created_at")
|
||||
inquiry_direction = request.GET.get("inquiry_direction", "incoming")
|
||||
sent_away = (
|
||||
Q(department=department)
|
||||
& Q(transferred_to_department__isnull=False)
|
||||
& ~Q(transferred_to_department=department)
|
||||
)
|
||||
incoming_inquiries_qs = Inquiry.objects.filter(
|
||||
Q(department=department, sent_to_department=True)
|
||||
| Q(outgoing_department=department)
|
||||
| Q(transferred_to_department=department)
|
||||
).exclude(sent_away)
|
||||
outgoing_inquiries_qs = Inquiry.objects.filter(sent_away)
|
||||
|
||||
if inquiry_direction == "outgoing":
|
||||
inquiries = outgoing_inquiries_qs
|
||||
else:
|
||||
inquiries = incoming_inquiries_qs
|
||||
inquiries = inquiries.select_related("assigned_to", "patient", "department", "outgoing_department", "transferred_to_department").order_by("-created_at")
|
||||
|
||||
incoming_count = incoming_inquiries_qs.count()
|
||||
outgoing_count = outgoing_inquiries_qs.count()
|
||||
all_inquiries_count = incoming_count + outgoing_count
|
||||
|
||||
observations = Observation.objects.filter(
|
||||
assigned_department=department, sent_to_department=True
|
||||
@ -2166,18 +2185,17 @@ def department_detail(request, pk):
|
||||
pending_inquiries = Inquiry.objects.filter(
|
||||
Q(outgoing_department=department) | Q(department=department, sent_to_department=True),
|
||||
transferred_at__isnull=False,
|
||||
department_responded_at__isnull=True,
|
||||
status__in=["open", "in_progress"],
|
||||
).order_by("dept_response_sla_due_at")
|
||||
for inq in pending_inquiries:
|
||||
pending_actions.append({
|
||||
"type": "inquiry_response",
|
||||
"type_label": _("Inquiry Response"),
|
||||
"type_label": _("Inquiry to Handle"),
|
||||
"reference": inq.reference_number or str(inq.id),
|
||||
"subject": inq.subject or inq.message[:80] if inq.message else "",
|
||||
"sla_due_at": inq.dept_response_sla_due_at,
|
||||
"is_overdue": inq.dept_response_sla_due_at and inq.dept_response_sla_due_at < dj_tz.now(),
|
||||
"url": "/inquiries/{}/department-response/".format(inq.pk),
|
||||
"url": "/organizations/departments/{}/inquiries/{}/".format(department.pk, inq.pk),
|
||||
"badge_color": "cyan",
|
||||
"item_id": str(inq.pk),
|
||||
"existing_en": inq.department_response_en or "",
|
||||
@ -2241,6 +2259,10 @@ def department_detail(request, pk):
|
||||
"staff_complaint_counts": staff_complaint_counts,
|
||||
"complaints": complaints[:5],
|
||||
"inquiries": inquiries[:5],
|
||||
"inquiry_direction": inquiry_direction,
|
||||
"incoming_count": incoming_count,
|
||||
"outgoing_count": outgoing_count,
|
||||
"all_inquiries_count": all_inquiries_count,
|
||||
"observations": observations[:5],
|
||||
"appreciations": appreciations[:50],
|
||||
"suggestions": suggestions[:50],
|
||||
@ -2423,7 +2445,7 @@ def department_complaints_list(request, pk):
|
||||
"complaint_dept_map": complaint_dept_map,
|
||||
"status_choices": Complaint.Status.choices if hasattr(Complaint, 'Status') else [],
|
||||
"list_stats": status_counts,
|
||||
"status_labels": {"open": _("Open"), "in_progress": _("In Progress"), "resolved": _("Resolved"), "closed": _("Closed")},
|
||||
"status_labels": {"open": _("Open"), "in_progress": _("In Progress"), "resolved": _("Resolved"), "closed": _("Closed"), "contact_no_response": _("Contacted - No Response")},
|
||||
}
|
||||
return render(request, "organizations/department_complaints.html", context)
|
||||
|
||||
@ -2442,9 +2464,26 @@ def department_inquiries_list(request, pk):
|
||||
messages.error(request, _("You don't have permission to view this department."))
|
||||
return redirect("organizations:department_list")
|
||||
|
||||
queryset = Inquiry.objects.filter(
|
||||
Q(department=department, sent_to_department=True) | Q(outgoing_department=department)
|
||||
).select_related("assigned_to", "patient").order_by("-created_at")
|
||||
direction = request.GET.get("direction", "incoming")
|
||||
|
||||
# Direction split (department POV):
|
||||
# outgoing = this department transferred the inquiry away to another department
|
||||
# incoming = inquiries this department currently handles (owned & not sent away, or transferred TO it)
|
||||
sent_away = (
|
||||
Q(department=department)
|
||||
& Q(transferred_to_department__isnull=False)
|
||||
& ~Q(transferred_to_department=department)
|
||||
)
|
||||
if direction == "outgoing":
|
||||
queryset = Inquiry.objects.filter(sent_away)
|
||||
else:
|
||||
queryset = Inquiry.objects.filter(
|
||||
Q(department=department, sent_to_department=True)
|
||||
| Q(outgoing_department=department)
|
||||
| Q(transferred_to_department=department)
|
||||
).exclude(sent_away)
|
||||
|
||||
queryset = queryset.select_related("assigned_to", "patient").order_by("-created_at")
|
||||
|
||||
from django.db.models import Count
|
||||
list_stats = queryset.values("status").annotate(count=Count("pk"))
|
||||
@ -2495,7 +2534,8 @@ def department_inquiries_list(request, pk):
|
||||
"date_to": date_to or "",
|
||||
"can_respond": can_respond,
|
||||
"list_stats": status_counts,
|
||||
"status_labels": {"open": _("Open"), "in_progress": _("In Progress"), "resolved": _("Resolved"), "closed": _("Closed")},
|
||||
"direction": direction,
|
||||
"status_labels": {"open": _("Open"), "in_progress": _("In Progress"), "resolved": _("Resolved"), "closed": _("Closed"), "contact_no_response": _("Contacted - No Response")},
|
||||
}
|
||||
return render(request, "organizations/department_inquiries.html", context)
|
||||
|
||||
@ -2731,12 +2771,8 @@ def department_inquiry_detail(request, pk, ipk):
|
||||
can_respond = (
|
||||
user.is_px_admin()
|
||||
or user.is_hospital_admin()
|
||||
or (user.is_department_manager() and user.department == department)
|
||||
or (
|
||||
user.is_champion()
|
||||
and user.department
|
||||
and user.department in [inquiry.department, inquiry.outgoing_department]
|
||||
)
|
||||
or (user.is_department_manager() and user.department_id == inquiry.handling_department_id)
|
||||
or (user.is_champion() and user.department_id == inquiry.handling_department_id)
|
||||
)
|
||||
|
||||
location_parts = []
|
||||
@ -2750,12 +2786,21 @@ def department_inquiry_detail(request, pk, ipk):
|
||||
inquiry_ct = ContentType.objects.get_for_model(Inquiry)
|
||||
notes = inquiry.notes.select_related("created_by").order_by("-created_at")
|
||||
|
||||
departments = (
|
||||
Department.objects.filter(hospital=department.hospital, status="active")
|
||||
.exclude(pk=department.pk)
|
||||
.order_by("name_en")
|
||||
)
|
||||
users = User.objects.filter(hospital=department.hospital, is_active=True)
|
||||
|
||||
context = {
|
||||
"department": department,
|
||||
"inquiry": inquiry,
|
||||
"location_str": " > ".join(location_parts) if location_parts else "",
|
||||
"can_respond": can_respond,
|
||||
"notes": notes,
|
||||
"departments": departments,
|
||||
"users": users,
|
||||
"content_type_id": inquiry_ct.pk,
|
||||
"object_id": inquiry.pk,
|
||||
}
|
||||
@ -3084,6 +3129,17 @@ def department_record_inquiry(request, pk):
|
||||
"created_at": inquiry.created_at.strftime("%Y-%m-%d %H:%M"),
|
||||
"due_at": inquiry.due_at.strftime("%Y-%m-%d %H:%M") if hasattr(inquiry, 'due_at') and inquiry.due_at else None,
|
||||
"ai_summary": inquiry.short_description_en if hasattr(inquiry, 'short_description_en') else "",
|
||||
"department_response_en": inquiry.department_response_en or "",
|
||||
"department_response_ar": inquiry.department_response_ar or "",
|
||||
"department_responded_at": inquiry.department_responded_at.strftime("%Y-%m-%d %H:%M") if inquiry.department_responded_at else None,
|
||||
"priority_display": inquiry.get_priority_display() if hasattr(inquiry, 'priority') and inquiry.priority else "",
|
||||
"contact_status_display": inquiry.get_contact_status_display() if inquiry.contact_status else "",
|
||||
"response": inquiry.response or inquiry.response_en or "",
|
||||
"responded_at": inquiry.responded_at.strftime("%Y-%m-%d %H:%M") if inquiry.responded_at else None,
|
||||
"responded_by": inquiry.responded_by.get_full_name() if inquiry.responded_by else None,
|
||||
"resolved_at": inquiry.resolved_at.strftime("%Y-%m-%d %H:%M") if inquiry.resolved_at else None,
|
||||
"resolved_by": inquiry.resolved_by.get_full_name() if inquiry.resolved_by else None,
|
||||
"outgoing_department": inquiry.outgoing_department.get_localized_name() if inquiry.outgoing_department else None,
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
|
||||
@ -928,7 +928,7 @@ def api_department_staff(request, department_id):
|
||||
|
||||
staff_qs = (
|
||||
Staff.objects.select_related("user")
|
||||
.filter(department=dept, hospital_id=dept.hospital_id, user__is_active=True)
|
||||
.filter(department=dept, hospital_id=dept.hospital_id, status="active")
|
||||
.order_by("first_name", "last_name")
|
||||
)
|
||||
|
||||
|
||||
@ -357,7 +357,33 @@ def presentation_generate(request):
|
||||
|
||||
@login_required
|
||||
def template_list(request):
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
templates = ReportTemplate.objects.select_related('created_by', 'hospital').prefetch_related('template_slides')
|
||||
|
||||
# Apply RBAC filters (previously missing entirely — every user saw all hospitals' templates)
|
||||
if user.is_px_admin():
|
||||
if selected_hospital:
|
||||
templates = templates.filter(Q(hospital=selected_hospital) | Q(hospital__isnull=True))
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
templates = templates.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
templates = templates.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
else:
|
||||
templates = templates.filter(hospital__isnull=True)
|
||||
elif user.is_department_manager() and user.department:
|
||||
templates = templates.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
elif user.is_champion() and user.department:
|
||||
templates = templates.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
elif user.is_source_user():
|
||||
templates = templates.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
templates = templates.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
else:
|
||||
templates = templates.filter(hospital__isnull=True)
|
||||
|
||||
context = {
|
||||
'templates': templates,
|
||||
'data_sources': REPORT_DATA_SOURCES,
|
||||
|
||||
@ -52,7 +52,7 @@ def _notify_project_team(project):
|
||||
if not recipient_ids:
|
||||
return
|
||||
|
||||
recipients = Staff.objects.filter(id__in=recipient_ids, user__isnull=False).select_related("user")
|
||||
recipients = Staff.objects.filter(id__in=recipient_ids).select_related("user")
|
||||
|
||||
subject = f"New QI Project: {project.name}"
|
||||
plain = (
|
||||
@ -71,7 +71,7 @@ def _notify_project_team(project):
|
||||
</div>
|
||||
"""
|
||||
for sp in recipients:
|
||||
email = getattr(sp.user, "email", None)
|
||||
email = sp.email or getattr(sp.user, "email", None)
|
||||
if not email:
|
||||
continue
|
||||
try:
|
||||
@ -1165,7 +1165,12 @@ def template_list(request):
|
||||
queryset = QIProject.objects.filter(is_template=True).select_related("hospital").prefetch_related("departments")
|
||||
|
||||
# Apply RBAC filters
|
||||
if not user.is_px_admin():
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
if user.is_px_admin():
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(Q(hospital=selected_hospital) | Q(hospital__isnull=True))
|
||||
else:
|
||||
# hospital_admin path (only px_admin/hospital_admin reach this point — gated above)
|
||||
queryset = queryset.filter(Q(hospital=user.hospital) | Q(hospital__isnull=True))
|
||||
|
||||
# Search
|
||||
|
||||
@ -513,10 +513,21 @@ def bulk_job_list(request):
|
||||
List all bulk survey jobs for the user.
|
||||
"""
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
# Filter jobs
|
||||
# Filter jobs by RBAC
|
||||
if user.is_px_admin():
|
||||
jobs = BulkSurveyJob.objects.all()
|
||||
if selected_hospital:
|
||||
jobs = BulkSurveyJob.objects.filter(hospital=selected_hospital)
|
||||
else:
|
||||
jobs = BulkSurveyJob.objects.none()
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
jobs = BulkSurveyJob.objects.filter(hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
jobs = BulkSurveyJob.objects.filter(hospital=user.hospital)
|
||||
else:
|
||||
jobs = BulkSurveyJob.objects.none()
|
||||
elif user.hospital:
|
||||
jobs = BulkSurveyJob.objects.filter(hospital=user.hospital)
|
||||
else:
|
||||
|
||||
@ -599,10 +599,22 @@ def survey_comments_list(request):
|
||||
|
||||
# Apply RBAC filters
|
||||
user = request.user
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
|
||||
if user.is_px_admin():
|
||||
pass # See all
|
||||
if selected_hospital:
|
||||
queryset = queryset.filter(survey_template__hospital=selected_hospital)
|
||||
elif user.is_hospital_admin() and user.hospital:
|
||||
queryset = queryset.filter(survey_template__hospital=user.hospital)
|
||||
elif user.is_px_management() or user.is_px_employee():
|
||||
if user.hospital:
|
||||
queryset = queryset.filter(survey_template__hospital=user.hospital)
|
||||
elif user.is_department_manager() and user.department:
|
||||
queryset = queryset.filter(survey_template__department=user.department)
|
||||
elif user.is_champion() and user.department:
|
||||
queryset = queryset.filter(survey_template__department=user.department)
|
||||
elif user.is_source_user():
|
||||
queryset = queryset.filter(created_by=user)
|
||||
elif user.hospital:
|
||||
queryset = queryset.filter(survey_template__hospital=user.hospital)
|
||||
else:
|
||||
@ -1817,8 +1829,19 @@ def survey_analytics_dashboard(request):
|
||||
messages.error(request, "You don't have permission to view analytics.")
|
||||
return redirect("surveys:instance_list")
|
||||
|
||||
# Get available templates
|
||||
templates = SurveyTemplate.objects.filter(is_active=True).order_by("name")
|
||||
# Get available templates (scoped to user's hospital; system-wide templates included)
|
||||
selected_hospital = getattr(request, "tenant_hospital", None)
|
||||
if user.is_px_admin():
|
||||
scope_hospital = selected_hospital
|
||||
else:
|
||||
scope_hospital = user.hospital
|
||||
|
||||
templates = SurveyTemplate.objects.filter(is_active=True)
|
||||
if scope_hospital:
|
||||
templates = templates.filter(Q(hospital=scope_hospital) | Q(hospital__isnull=True))
|
||||
else:
|
||||
templates = templates.filter(hospital__isnull=True)
|
||||
templates = templates.order_by("name")
|
||||
|
||||
# Get selected template from query param
|
||||
selected_template_id = request.GET.get("template")
|
||||
|
||||
@ -441,7 +441,7 @@ DEFAULT_FROM_EMAIL = env("DEFAULT_FROM_EMAIL", default="noreply@px360.sa")
|
||||
SITE_URL = env("SITE_URL", default="http://localhost:8000")
|
||||
BASE_URL = SITE_URL
|
||||
SURVEY_BASE_URL = SITE_URL
|
||||
EMAIL_LOGO_URL = env("EMAIL_LOGO_URL", default=f"{SITE_URL}{STATIC_URL}img/HH_P_H_Logo.png")
|
||||
EMAIL_LOGO_URL = env("EMAIL_LOGO_URL", default=f"{SITE_URL}{STATIC_URL}images/logos/HH_P_H_Logo_CMYK---LANDSCIP1.png")
|
||||
|
||||
# HIS Integration Settings
|
||||
HIS_API_URL = env("HIS_API_URL", default="https://his.alhammadi.med.sa/SSRCE/API/FetchPatientVisitTimeStamps")
|
||||
|
||||
@ -6,7 +6,7 @@ from .base import * # noqa
|
||||
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ["192.168.8.13","localhost", "127.0.0.1", "0.0.0.0", ".ngrok-free.app", "micha-nonparabolic-lovie.ngrok-free.dev"]
|
||||
ALLOWED_HOSTS = ["192.168.8.13","localhost", "127.0.0.1", "0.0.0.0"]
|
||||
|
||||
# Database - Use PostgreSQL even in dev for consistency
|
||||
# Override with SQLite if needed for quick local testing
|
||||
@ -17,11 +17,7 @@ ALLOWED_HOSTS = ["192.168.8.13","localhost", "127.0.0.1", "0.0.0.0", ".ngrok-fre
|
||||
# )
|
||||
# }
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
"https://*.ngrok-free.app",
|
||||
"https://*.ngrok.app",
|
||||
"https://micha-nonparabolic-lovie.ngrok-free.dev",
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = []
|
||||
|
||||
# Database inherits from base.py (PostgreSQL)
|
||||
# To temporarily switch back to SQLite for testing, uncomment:
|
||||
|
||||
78
deploy/compose.dev-home.yml
Normal file
78
deploy/compose.dev-home.yml
Normal file
@ -0,0 +1,78 @@
|
||||
# PX360 — DEV compose for `target deploy` (bundled postgres + redis)
|
||||
#
|
||||
# target injects into the `web_app` service on deploy:
|
||||
# - container_name: <app>-web_app (so the shared Caddy can route to it)
|
||||
# - networks: [target-proxy, default]
|
||||
# - removes any `ports:` (the shared Caddy handles ingress)
|
||||
#
|
||||
# The web service MUST be named `web_app` — target's Caddy injection looks for it.
|
||||
# `{{ .ImageTag }}` is replaced by target with the freshly built image tag.
|
||||
#
|
||||
# env file (in the deploy dir ~/.target/hh-dev/):
|
||||
# .env — ALL config + secrets, shipped by `target deploy` (deploy/env.dev)
|
||||
services:
|
||||
web_app:
|
||||
image: {{ .ImageTag }}
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- media_volume:/app/media
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8888:8000"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
celery:
|
||||
image: {{ .ImageTag }}
|
||||
restart: unless-stopped
|
||||
command: celery -A config worker -l info --concurrency=2
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
celery-beat:
|
||||
image: {{ .ImageTag }}
|
||||
restart: unless-stopped
|
||||
command: celery -A config beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-px360}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
redis_data:
|
||||
media_volume:
|
||||
@ -24,7 +24,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "8888:8000"
|
||||
- "8000:8000"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health/"]
|
||||
interval: 30s
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
# Usage: `make deploy-dev` (runs: target deploy --config deploy/target.dev.yaml)
|
||||
app:
|
||||
name: "hh-dev" # isolated project dir: ~/.target/hh-dev/
|
||||
domain: "px360.ismailmosaibrahim.com" # shared Caddy routes this domain → web_app
|
||||
port: 8888
|
||||
domain: "px360.hh.med.sa" # shared Caddy routes this domain → web_app
|
||||
port: 8000
|
||||
|
||||
server:
|
||||
ip: "192.168.8.73"
|
||||
user: "ismail"
|
||||
ip: "10.132.16.5"
|
||||
user: "ahh-ops"
|
||||
ssh_key: "~/.ssh/id_rsa"
|
||||
|
||||
build:
|
||||
@ -15,7 +15,7 @@ build:
|
||||
context: "." # repo root (where the Dockerfile lives)
|
||||
|
||||
deploy:
|
||||
use_caddy: false # shared target-caddy handles HTTPS
|
||||
use_caddy: false # shared target-caddy handles HTTPS
|
||||
caddy_email: "ismail.mosa.ibrahim@gmail.com"
|
||||
compose_file: "deploy/compose.dev.yml" # user-provided compose (uses {{ .ImageTag }})
|
||||
env_file: "deploy/env.dev" # shipped to ~/.target/hh-dev/.env each deploy
|
||||
|
||||
@ -705,3 +705,4 @@ For interactive API documentation with try-it-out functionality:
|
||||
---
|
||||
|
||||
**Last Updated:** December 14, 2025
|
||||
|
||||
|
||||
@ -744,7 +744,7 @@
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"hospital_id\": 1,\n \"doctor_id\": \"10738\",\n \"doctor_name\": \"Dr. Omaymah Yaqoub\",\n \"rating\": 4,\n \"feedback\": \"Great doctor, very caring and attentive.\",\n \"rating_date\": \"2026-06-10\",\n \"patient_uhid\": \"UHID12345\",\n \"patient_name\": \"Ahmed Ali\",\n \"patient_type\": \"OP\",\n \"department_name\": \"Internal Medicine\"\n}"
|
||||
"raw": "{\n \"hospital\": \"Al Nuzha\",\n \"doctor_id\": \"10738\",\n \"doctor_name\": \"Dr. Omaymah Yaqoub\",\n \"rating\": 4,\n \"feedback\": \"Great doctor, very caring and attentive.\",\n \"rating_date\": \"2026-06-10\",\n \"patient_uhid\": \"UHID12345\",\n \"patient_name\": \"Ahmed Ali\",\n \"patient_type\": \"OP\",\n \"department_name\": \"Internal Medicine\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{base_url}}/api/v1/external/doctor-ratings/",
|
||||
@ -765,7 +765,7 @@
|
||||
},
|
||||
"status": "Created",
|
||||
"code": 201,
|
||||
"body": "{\n \"success\": true,\n \"rating_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"staff_id\": \"f1e2d3c4-b5a6-7890-abcd-ef1234567890\",\n \"doctor_name\": \"OMAYMAH YAQOUB ELAMEIAN\",\n \"rating\": 4\n}"
|
||||
"body": "{\n \"success\": true,\n \"rating_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"rating\": 4\n}"
|
||||
},
|
||||
{
|
||||
"name": "Doctor Not Found",
|
||||
|
||||
@ -545,7 +545,7 @@ GET /api/v1/external/suggestions/<reference_number>/
|
||||
|
||||
### Submit a Doctor Rating
|
||||
|
||||
Submit a patient rating for a doctor. The `doctor_id` must be an existing employee ID in the Staff table for the given hospital.
|
||||
Submit a patient rating for a doctor. The `doctor_id` should identify the doctor (existing staff matched by employee ID, license number, or name; otherwise a new Staff record is auto-created — see below).
|
||||
|
||||
```
|
||||
POST /api/v1/external/doctor-ratings/
|
||||
@ -555,10 +555,10 @@ POST /api/v1/external/doctor-ratings/
|
||||
|
||||
| Field | Required | Type | Description |
|
||||
|---|---|---|---|
|
||||
| `hospital_id` | Yes | integer | Hospital ID |
|
||||
| `doctor_id` | Yes | string | Doctor's employee ID (must exist in the hospital's Staff records) |
|
||||
| `hospital` | Yes | string | Hospital name (case-insensitive) |
|
||||
| `doctor_id` | Yes | string | Doctor's employee ID |
|
||||
| `rating` | Yes | integer | Rating from 1 to 5 |
|
||||
| `doctor_name` | No | string | Doctor name (stored for reference) |
|
||||
| `doctor_name` | Conditional | string | Doctor name. Required when `doctor_id` does not match an existing staff record (see auto-creation below). Otherwise stored for reference. |
|
||||
| `feedback` | No | string | Patient feedback text |
|
||||
| `rating_date` | No | date | Date of rating (`YYYY-MM-DD`, defaults to current date) |
|
||||
| `patient_uhid` | No | string | Patient unique health ID |
|
||||
@ -568,11 +568,13 @@ POST /api/v1/external/doctor-ratings/
|
||||
| `admit_date` | No | date | Admission date (`YYYY-MM-DD`) |
|
||||
| `discharge_date` | No | date | Discharge date (`YYYY-MM-DD`) |
|
||||
|
||||
**Staff lookup & auto-creation:** When a rating is submitted, the server looks up the doctor by (in order) `employee_id`, `license_number`, then `doctor_name` (exact / contains / first+last name split). If no existing staff is matched, a new Staff record is **auto-created** with `staff_type=physician`, `job_title="Physician"`, `employee_id=doctor_id`, and the provided name. In that case `doctor_name` is required; omitting it returns `400`. The auto-creation is logged as an `external_staff_auto_created` audit event.
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"hospital_id": 1,
|
||||
"hospital": "Al Nuzha",
|
||||
"doctor_id": "10738",
|
||||
"doctor_name": "Dr. Omaymah Yaqoub",
|
||||
"rating": 4,
|
||||
@ -591,8 +593,6 @@ POST /api/v1/external/doctor-ratings/
|
||||
{
|
||||
"success": true,
|
||||
"rating_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"staff_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
|
||||
"doctor_name": "OMAYMAH YAQOUB ELAMEIAN",
|
||||
"rating": 4
|
||||
}
|
||||
```
|
||||
@ -601,21 +601,95 @@ POST /api/v1/external/doctor-ratings/
|
||||
|
||||
| HTTP Status | Condition |
|
||||
|---|---|
|
||||
| `400` | `doctor_id` not found in the hospital's Staff records |
|
||||
| `400` | `doctor_id` matches no existing staff AND `doctor_name` is missing (cannot auto-create) |
|
||||
| `400` | `rating` outside 1-5 range |
|
||||
| `400` | `hospital_id` not found |
|
||||
| `400` | `hospital` not found |
|
||||
| `403` | Hospital does not match API key scope |
|
||||
|
||||
**Error Example (doctor not found):**
|
||||
**Error Example (cannot auto-create without a name):**
|
||||
|
||||
```json
|
||||
{
|
||||
"doctor_id": [
|
||||
"Doctor with employee ID '99999' not found at this hospital."
|
||||
"doctor_name": [
|
||||
"Staff not found for this doctor_id; provide doctor_name to auto-create the staff record."
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### List Doctor Ratings
|
||||
|
||||
Returns a paginated list of doctor ratings, scoped to the API key's hospital.
|
||||
|
||||
```
|
||||
GET /api/v1/external/doctor-ratings/list/
|
||||
```
|
||||
|
||||
**Query Parameters (all optional):**
|
||||
|
||||
| Parameter | Description |
|
||||
|---|---|
|
||||
| `doctor_id` | Filter by HIS employee ID (exact match) |
|
||||
| `rating` | Filter by rating value 1-5 (exact match) |
|
||||
| `rating_from` | Rating-date lower bound (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`) |
|
||||
| `rating_to` | Rating-date upper bound (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`) |
|
||||
| `created_from` / `created_to` | Created-at date range |
|
||||
| `updated_from` / `updated_to` | Updated-at date range |
|
||||
| `page` | Page number (default `1`) |
|
||||
| `page_size` | Page size (default `20`, max `100`) |
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 42,
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
"results": [
|
||||
{
|
||||
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"staff_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
|
||||
"hospital_name": "Al Nuzha",
|
||||
"source": "his_api",
|
||||
"source_reference": "",
|
||||
"doctor_id": "10738",
|
||||
"doctor_name": "OMAYMAH YAQOUB ELAMEIAN",
|
||||
"doctor_name_raw": "10738 - OMAYMAH YAQOUB ELAMEIAN",
|
||||
"doctor_name_display": "OMAYMAH YAQOUB ELAMEIAN",
|
||||
"department_name": "Internal Medicine",
|
||||
"patient_uhid": "UHID12345",
|
||||
"patient_name": "Ahmed Ali",
|
||||
"patient_type": "OP",
|
||||
"admit_date": null,
|
||||
"discharge_date": null,
|
||||
"rating": 4,
|
||||
"feedback": "Great doctor, very caring and attentive.",
|
||||
"rating_date": "2026-06-10T00:00:00+03:00",
|
||||
"is_aggregated": false,
|
||||
"aggregated_at": null,
|
||||
"created_at": "2026-06-10T14:22:11+03:00",
|
||||
"updated_at": "2026-06-10T14:22:11+03:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Retrieve a Doctor Rating
|
||||
|
||||
Retrieve a single rating by its UUID (returned in the `rating_id` field on creation).
|
||||
|
||||
```
|
||||
GET /api/v1/external/doctor-ratings/<rating_id>/
|
||||
```
|
||||
|
||||
**Response (200):** Same object shape as a single item from the list endpoint.
|
||||
|
||||
**Error Responses:**
|
||||
|
||||
| HTTP Status | Condition |
|
||||
|---|---|
|
||||
| `404` | Rating does not exist, or is outside the API key's hospital scope |
|
||||
| `403` | API key does not have access to `doctor_ratings` |
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
506
exports/admin_notifications.html
Normal file
506
exports/admin_notifications.html
Normal file
@ -0,0 +1,506 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Admin Notification Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 1: NEW COMPLAINT ADMIN NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ New Complaint (Admin Notification)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى إدارة تجربة المريض</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم باستلام شكوى جديدة بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">20/يوليو/2026 10:45 ص</a> برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0067</a> الأولوية: <strong>عالية</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع عليها وتفعيلها، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(تفعيل الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو مراجعتها وتفعيلها في أقرب وقت ممكن، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الإفادة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Patient Experience Management</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that a new complaint has been received on: <a href="#" style="color: #0056b3; text-decoration: underline;">20/July/2026 10:45 AM</a> with reference: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0067</a> Priority: <strong>High</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view and activate it, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Activate Complaint)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please review and activate it at your earliest convenience, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Feedback Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 2: COMMUNICATION REQUEST -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Communication Request
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم علاقات المرضى</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بطلب تواصل جديد بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">20/يوليو/2026 01:15 م</a> من قبل المستخدم: <a href="#" style="color: #0056b3; text-decoration: underline;">نورة القحطاني</a> رقم الطلب: <a href="#" style="color: #0056b3; text-decoration: underline;">CR-2026-0158</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على الطلب والرد عليه، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الطلب)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو الرد على الطلب في أقرب وقت، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Patient Relations Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that a new communication request has been submitted on: <a href="#" style="color: #0056b3; text-decoration: underline;">20/July/2026 01:15 PM</a> by user: <a href="#" style="color: #0056b3; text-decoration: underline;">Noura Al-Qahtani</a> Request ID: <a href="#" style="color: #0056b3; text-decoration: underline;">CR-2026-0158</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view and respond to the request, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Request Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please respond to the request at your earliest convenience, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Reply Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 3: INVITATION EXPIRED -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Invitation Expired
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / محمد العمري</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بأن <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">دعوتكم للانضمام إلى نظام PX360 قد انتهت صلاحيتها</span> بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">19/يوليو/2026 11:59 م</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
لطلب دعوة جديدة، يرجى التواصل مع المسؤول، وللإطلاع إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(طلب دعوة جديدة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
سيتم إنشاء رابط دعوة جديد لكم لإكمال إعداد الحساب، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الإفادة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Mohammed Al-Omari</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">your invitation to join PX360 has expired</span> on: <a href="#" style="color: #0056b3; text-decoration: underline;">19/July/2026 11:59 PM</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To request a new invitation, please contact your administrator, and to view, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Request New Invitation)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
A new invitation link will be generated for you to complete your account setup, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Feedback Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 4: PX360 WEEKLY DIGEST -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ PX360 Weekly Digest
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">ملخص الأسبوع - مستشفى الحمادي</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بالملخص الأسبوعي لأداء تجربة المريض للأسبوع: <a href="#" style="color: #0056b3; text-decoration: underline;">14-20 يوليو 2026</a> إجمالي الشكاوى: <strong>24</strong> | معدل الالتزام بـ SLA: <strong>87%</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على التفاصيل الكاملة، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(لوحة المعلومات)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو مراجعة المؤشرات والتنبيهات والرد على المخاطر العالية، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الإفادة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">Weekly Digest - Al Hammadi Hospital</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to share the weekly patient experience performance digest for the week: <a href="#" style="color: #0056b3; text-decoration: underline;">14-20 July 2026</a> Total Complaints: <strong>24</strong> | SLA Compliance: <strong>87%</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the full dashboard, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Dashboard)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please review the metrics, early warnings, and respond to high-risk items, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Feedback Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
140
exports/appointment_email.html
Normal file
140
exports/appointment_email.html
Normal file
@ -0,0 +1,140 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Appointment Confirmation Template</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 1: APPOINTMENT CONFIRMATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Appointment Confirmation
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى المريض / ماجد الحربي</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بتأكيد موعدكم بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">25/يوليو/2026 10:30 ص</a> رقم الموعد: <a href="#" style="color: #0056b3; text-decoration: underline;">APT-202607-0214</a> القسم: <strong>العيادات الخارجية - الباطنة</strong> الطبيب: <strong>د. سامي إبراهيم</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على تفاصيل الموعد، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(تفاصيل الموعد)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو الحضور قبل الموعد بـ 15 دقيقة لإتمام التسجيل. لإعادة الجدولة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(إعادة الجدولة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Patient / Majed Al-Harbi</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to confirm your appointment on: <a href="#" style="color: #0056b3; text-decoration: underline;">25/July/2026 10:30 AM</a> Appointment ID: <a href="#" style="color: #0056b3; text-decoration: underline;">APT-202607-0214</a> Department: <strong>Outpatient - Internal Medicine</strong> Doctor: <strong>Dr. Sami Ibrahim</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the appointment details, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Appointment Details)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please arrive 15 minutes before the appointment for registration. To reschedule, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Reschedule)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
311
exports/complaint_escalation_artboard_variant.html
Normal file
311
exports/complaint_escalation_artboard_variant.html
Normal file
@ -0,0 +1,311 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Complaint Escalation - Artboard Variant</title>
|
||||
<style>
|
||||
/* ===================================================================
|
||||
ARTBOARD VARIANT — Fixed A4 page per stage
|
||||
Each card is a 210mm × 297mm page with the hospital letterhead
|
||||
as its background. The artboard's header/footer artwork shows
|
||||
correctly because the page dimensions match its aspect ratio.
|
||||
=================================================================== */
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
background-color: #e8eaed;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
padding: 40px 10px;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stage-label {
|
||||
max-width: 210mm;
|
||||
margin: 0 auto 15px auto;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #4b5563;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.artboard-page {
|
||||
position: relative;
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
margin: 0 auto 30mm auto;
|
||||
background-image: url("../static/images/artboard/Artboard 1@3x.png");
|
||||
background-size: 210mm 297mm;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top left;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||
border: 1px solid #d1d5db;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Content zone — flows from below the header artwork (38mm) to
|
||||
above the footer artwork (32mm). No absolute positioning needed;
|
||||
content flows naturally top-to-bottom within the padded area.
|
||||
Matches the proven pattern from templates/appreciation/appreciation_letter.html. */
|
||||
.content {
|
||||
padding: 38mm 20mm 32mm 20mm;
|
||||
height: 297mm;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ar-block { direction: rtl; text-align: center; margin-bottom: 4mm; }
|
||||
.en-block { direction: ltr; text-align: center; }
|
||||
|
||||
.ar-block h1 {
|
||||
font-size: 22pt;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
margin-bottom: 8mm;
|
||||
}
|
||||
|
||||
.en-block h1 {
|
||||
font-size: 22pt;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
margin-bottom: 8mm;
|
||||
}
|
||||
|
||||
.ar-block p, .en-block p {
|
||||
font-size: 13pt;
|
||||
line-height: 1.8;
|
||||
color: #111827;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5mm;
|
||||
}
|
||||
|
||||
.link-red {
|
||||
color: #dc2626;
|
||||
font-weight: bold;
|
||||
font-size: 14pt;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.link-blue {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #ffff00;
|
||||
padding: 0 4px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: 0;
|
||||
border-top: 1px dashed #d1d5db;
|
||||
margin: 6mm 0;
|
||||
}
|
||||
|
||||
.btn-teal {
|
||||
display: inline-block;
|
||||
padding: 12px 30px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #004b69;
|
||||
text-decoration: none;
|
||||
background-color: #008cc1;
|
||||
border-radius: 2px;
|
||||
margin-top: 5mm;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
font-size: 12pt;
|
||||
line-height: 1.6;
|
||||
color: #111827;
|
||||
font-weight: 600;
|
||||
margin-top: 5mm;
|
||||
}
|
||||
|
||||
.signoff {
|
||||
font-size: 13pt;
|
||||
color: #1f2937;
|
||||
font-weight: 600;
|
||||
margin-top: 6mm;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="page-wrapper">
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- STAGE 1: NEW COMPLAINT NOTIFICATION -->
|
||||
<!-- ================================================================ -->
|
||||
<div class="stage-label">▼ Stage 1: New Complaint Notification</div>
|
||||
<div class="artboard-page">
|
||||
<div class="content">
|
||||
|
||||
<div class="ar-block">
|
||||
<h1>إلى قسم المختبر</h1>
|
||||
<p>تحية طيبة</p>
|
||||
<p>
|
||||
نود إبلاغكم باستلام شكوى بتاريخ:
|
||||
<a href="#" class="link-blue">23/يونيو/2026 12:15 م</a>
|
||||
من قبل وزارة الصحة برقم الهوية:
|
||||
<a href="#" class="link-blue">1113543183</a>
|
||||
</p>
|
||||
<p>للإطلاع عليها، يمكنكم النقر على</p>
|
||||
<p><a href="#" class="link-red">(نموذج الشكوى)</a></p>
|
||||
<p>نرجو النظر في الأمر والتحقق خلال 24 ساعة، وللإفادة إضغط على</p>
|
||||
<p><a href="#" class="link-red">(نموذج الرد على الشكوى)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p>رقم التواصل: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">رابط</a>
|
||||
<p class="signoff">نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض</p>
|
||||
</div>
|
||||
|
||||
<hr class="divider" />
|
||||
|
||||
<div class="en-block">
|
||||
<h1>To: Laboratory Department</h1>
|
||||
<p>Greetings,</p>
|
||||
<p>
|
||||
We would like to inform you that a complaint has been received on:
|
||||
<a href="#" class="link-blue">23/June/2026 12:15 PM</a>
|
||||
by the Ministry of Health with ID number:
|
||||
<a href="#" class="link-blue">1113543183</a>
|
||||
</p>
|
||||
<p>To view it, you can click on:</p>
|
||||
<p><a href="#" class="link-red">(Complaint Form)</a></p>
|
||||
<p>Please look into the matter and verify within 24 hours, and to provide feedback, click on:</p>
|
||||
<p><a href="#" class="link-red">(Complaint Response Form)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>For inquiries or more information, via Extension: 1379</p>
|
||||
<p>Contact Number: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">Link</a>
|
||||
<p class="signoff">We appreciate your commitment.<br />Regards, Patient Relations and Experience Management</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- STAGE 2: FIRST REMINDER NOTIFICATION -->
|
||||
<!-- ================================================================ -->
|
||||
<div class="stage-label">▼ Stage 2: First Reminder Notification</div>
|
||||
<div class="artboard-page">
|
||||
<div class="content">
|
||||
|
||||
<div class="ar-block">
|
||||
<h1>إلى قسم الأمن</h1>
|
||||
<p>
|
||||
نقدر لكم انشغالكم وحرصكم، نود
|
||||
<span class="highlight">تذكيركم</span>
|
||||
بالشكوى المقدمة
|
||||
بتاريخ: <a href="#" class="link-blue">21/يونيو/2026 02:23 م</a>
|
||||
من قبل المراجع برقم الهوية:
|
||||
<a href="#" class="link-blue">2343965287</a>
|
||||
</p>
|
||||
<p><a href="#" class="link-red">(نموذج الشكوى)</a></p>
|
||||
<p>نرجو منكم التكرم بالرد على هذه الشكوى خلال الـ 24 ساعة القادمة، حرصاً على تحسين تجربة المريض وضمان معالجة الشكوى في الوقت المناسب، للرد نأمل النقر على</p>
|
||||
<p><a href="#" class="link-red">(نموذج الرد على الشكوى)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل على:</p>
|
||||
<p>التحويلة: 1379 — رقم التواصل: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">رابط</a>
|
||||
<p class="signoff">نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض</p>
|
||||
</div>
|
||||
|
||||
<hr class="divider" />
|
||||
|
||||
<div class="en-block">
|
||||
<h1>To: Security Department</h1>
|
||||
<p>
|
||||
We appreciate your dedication and diligence. We would like to
|
||||
<span class="highlight">remind you</span>
|
||||
regarding the submitted complaint
|
||||
dated: <a href="#" class="link-blue">21/June/2026 02:23 PM</a>
|
||||
by the visitor with National ID:
|
||||
<a href="#" class="link-blue">2343965287</a>
|
||||
</p>
|
||||
<p><a href="#" class="link-red">(Complaint Form)</a></p>
|
||||
<p>Please kindly respond to this complaint within the next 24 hours, in our commitment to enhancing patient experience and ensuring timely resolution. To reply, please click on:</p>
|
||||
<p><a href="#" class="link-red">(Complaint Response Form)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>For inquiries or more information, please contact us via:</p>
|
||||
<p>Extension: 1379 — Contact Number: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">Link</a>
|
||||
<p class="signoff">We appreciate your commitment.<br />Regards, Patient Relations and Experience Management</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ================================================================ -->
|
||||
<!-- STAGE 3: SECOND REMINDER NOTIFICATION -->
|
||||
<!-- ================================================================ -->
|
||||
<div class="stage-label">▼ Stage 3: Second Reminder Notification</div>
|
||||
<div class="artboard-page">
|
||||
<div class="content">
|
||||
|
||||
<div class="ar-block">
|
||||
<h1>إلى قسم المالية</h1>
|
||||
<p>تحية طيبة</p>
|
||||
<p>
|
||||
نشكر لكم تعاونكم ونقدّر انشغالكم، ولكن نرجو
|
||||
<span class="highlight">ملاحظة الوقت المتبقي للرد هو 6 ساعات</span>
|
||||
على الشكوى المقدمة بتاريخ:
|
||||
<a href="#" class="link-blue">22/يونيو/2026 04:20 م</a>
|
||||
من قبل مجلس الضمان الصحي برقم الهوية:
|
||||
<a href="#" class="link-blue">1050208576</a>
|
||||
</p>
|
||||
<p><a href="#" class="link-red">(نموذج الشكوى)</a></p>
|
||||
<p>لذا نأمل منكم التكرم بالرد والإفادة في أقرب وقت ممكن لتجنب أي تأخير إضافي في معالجة الشكوى. وللرد، يرجى النقر على</p>
|
||||
<p><a href="#" class="link-red">(نموذج الرد على الشكوى)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل معنا:</p>
|
||||
<p>التحويلة: 1379 — رقم التواصل: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">رابط</a>
|
||||
<p class="signoff">نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض</p>
|
||||
</div>
|
||||
|
||||
<hr class="divider" />
|
||||
|
||||
<div class="en-block">
|
||||
<h1>To: Financial Department</h1>
|
||||
<p>Greetings,</p>
|
||||
<p>
|
||||
We appreciate your cooperation and value your time, but please
|
||||
<span class="highlight">note that the remaining time to respond is 6 hours</span>
|
||||
regarding the complaint submitted on:
|
||||
<a href="#" class="link-blue">22/June/2026 04:20 PM</a>
|
||||
by the Council of Health Insurance with ID number:
|
||||
<a href="#" class="link-blue">1050208576</a>
|
||||
</p>
|
||||
<p><a href="#" class="link-red">(Complaint Form)</a></p>
|
||||
<p>Therefore, we kindly request your response and feedback as soon as possible to avoid any further delay in processing the complaint. To reply, please click on:</p>
|
||||
<p><a href="#" class="link-red">(Complaint Response Form)</a></p>
|
||||
<div class="contact-info">
|
||||
<p>For inquiries or more information, please contact us via:</p>
|
||||
<p>Extension: 1379 — Contact Number: <a href="tel:0537695000" class="link-blue">0537695000</a></p>
|
||||
</div>
|
||||
<a href="#" class="btn-teal">Link</a>
|
||||
<p class="signoff">We appreciate your commitment.<br />Regards, Patient Relations and Experience Management</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
383
exports/complaint_escalation_templates.html
Normal file
383
exports/complaint_escalation_templates.html
Normal file
@ -0,0 +1,383 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Complaint System Escalation Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 1: NEW COMPLAINT NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<!-- Template Title Divider -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 1: New Complaint Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم المختبر</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم باستلام شكوى بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">23/يونيو/2026 12:15 م</a> من قبل وزارة الصحة برقم الهوية: <a href="#" style="color: #0056b3; text-decoration: underline;">1113543183</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للإطلاع عليها، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو النظر في الأمر والتحقق خلال 24 ساعة، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Laboratory Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that a complaint has been received on: <a href="#" style="color: #0056b3; text-decoration: underline;">23/June/2026 12:15 PM</a> by the Ministry of Health with ID number: <a href="#" style="color: #0056b3; text-decoration: underline;">1113543183</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view it, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please look into the matter and verify within 24 hours, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 2: FIRST REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<!-- Template Title Divider -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 2: First Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الأمن</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نقدر لكم انشغالكم وحرصكم، نود
|
||||
<span style="background-color: #ffff00; padding: 0 4px; color: #000000;">تذكيركم</span>
|
||||
بالشكوى المقدمة
|
||||
<br />
|
||||
بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">21/يونيو/2026 02:23 م</a>
|
||||
<br />
|
||||
من قبل المراجع برقم الهوية: <a href="#" style="color: #0056b3; text-decoration: underline;">2343965287</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو منكم التكرم بالرد على هذه الشكوى خلال الـ 24 ساعة القادمة، حرصاً على تحسين تجربة المريض وضمان معالجة الشكوى في الوقت المناسب , للرد نأمل النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل على:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin:;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 40px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 40px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Security Department</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your dedication and diligence. We would like to <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">remind you</span> regarding the submitted complaint
|
||||
<br />
|
||||
Dated: <a href="#" style="color: #0056b3; text-decoration: underline;">21/June/2026 02:23 PM</a>
|
||||
<br />
|
||||
By the visitor with National ID: <a href="#" style="color: #0056b3; text-decoration: underline;">2343965287</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please kindly respond to this complaint within the next 24 hours, in our commitment to enhancing patient experience and ensuring timely resolution. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin:;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 20px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 3: SECOND REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<!-- Template Title Divider -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 3: Second Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم المالية</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نشكر لكم تعاونكم ونقدّر انشغالكم، ولكن نرجو <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">ملاحظة الوقت المتبقي للرد هو 6 ساعات</span> على الشكوى المقدمة بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">22/يونيو/2026 04:20 م</a> من قبل مجلس الضمان الصحي برقم الهوية: <a href="#" style="color: #0056b3; text-decoration: underline;">1050208576</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
لذا نأمل منكم التكرم بالرد والإفادة في أقرب وقت ممكن لتجنب أي تأخير إضافي في معالجة الشكوى. وللرد، يرجى النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل معنا:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Financial Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your cooperation and value your time, but please <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">note that the remaining time to respond is 6 hours</span> regarding the complaint submitted on: <a href="#" style="color: #0056b3; text-decoration: underline;">22/June/2026 04:20 PM</a> by the Council of Health Insurance with ID number: <a href="#" style="color: #0056b3; text-decoration: underline;">1050208576</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Therefore, we kindly request your response and feedback as soon as possible to avoid any further delay in processing the complaint. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
502
exports/explanation_requests.html
Normal file
502
exports/explanation_requests.html
Normal file
@ -0,0 +1,502 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Explanation Request Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 1: EXPLANATION REQUEST (COMPLAINT) -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Explanation Request (Complaint)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الطوارئ</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بإسناد شكوى لقسمكم للرد عليها، بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">20/يوليو/2026 09:00 ص</a> برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0072</a> الموعد النهائي: <a href="#" style="color: #0056b3; text-decoration: underline;">22/يوليو/2026 05:00 م</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على تفاصيل الشكوى، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو تقديم رد القسم قبل الموعد النهائي، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Emergency Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Your department has been assigned to respond to a complaint, dated: <a href="#" style="color: #0056b3; text-decoration: underline;">20/July/2026 09:00 AM</a> with reference: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0072</a> Deadline: <a href="#" style="color: #0056b3; text-decoration: underline;">22/July/2026 05:00 PM</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the complaint details, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please submit your department's response before the deadline, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 2: EXPLANATION REMINDER -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Explanation Reminder (First)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / سعد الدوسري</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نقدر لكم انشغالكم وحرصكم، نود
|
||||
<span style="background-color: #ffff00; padding: 0 4px; color: #000000;">تذكيركم</span>
|
||||
بتقديم رد القسم على الشكوى
|
||||
<br />
|
||||
بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">19/يوليو/2026 11:00 ص</a>
|
||||
<br />
|
||||
برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0069</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو منكم التكرم بتقديم الشرح خلال الـ 24 ساعة القادمة لتجنب التصعيد إلى مديركم المباشر , للرد نأمل النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل على:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin:;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 40px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 40px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Saad Al-Dosari</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your dedication and diligence. We would like to <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">remind you</span> to submit your department's response regarding the complaint
|
||||
<br />
|
||||
Dated: <a href="#" style="color: #0056b3; text-decoration: underline;">19/July/2026 11:00 AM</a>
|
||||
<br />
|
||||
Reference: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0069</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please kindly submit your explanation within the next 24 hours to avoid escalation to your direct manager. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin:;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 20px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 3: EXPLANATION SECOND REMINDER (FINAL) -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Explanation Second Reminder (Final)
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / فهد الشمري</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نشكر لكم تعاونكم، ولكن نرجو <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">الملاحظة بأن هذا تذكيرك النهائي والعاجل</span> لتقديم الشرح بخصوص الشكوى بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">18/يوليو/2026 02:30 م</a> برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0065</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الشكوى)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
إذا لم يتم تقديم الشرح قبل الموعد النهائي، سيتم تصعيد الأمر إلى مديركم المباشر. يرجى التقديم فوراً
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل معنا:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Fahad Al-Shammari</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your cooperation, but please <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">note that this is your FINAL and URGENT reminder</span> to submit your explanation regarding the complaint dated: <a href="#" style="color: #0056b3; text-decoration: underline;">18/July/2026 02:30 PM</a> with reference: <a href="#" style="color: #0056b3; text-decoration: underline;">CMP-202607-0065</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Complaint Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
If the explanation is not submitted before the deadline, this matter will be escalated to your direct manager. Please submit immediately:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 4: INQUIRY EXPLANATION REQUEST -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Inquiry Explanation Request
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / عبدالله الغامدي</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بطلب رد على استفسار بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">20/يوليو/2026 08:00 ص</a> برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0021</a> الموضوع: <strong>تأخر موعد موعد العيادة</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على تفاصيل الاستفسار، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو تقديم ردكم في أقرب وقت ممكن، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Abdullah Al-Ghamdi</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
You have been requested to provide a response regarding an inquiry, dated: <a href="#" style="color: #0056b3; text-decoration: underline;">20/July/2026 08:00 AM</a> with reference: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0021</a> Subject: <strong>Clinic appointment delay</strong>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the inquiry details, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please submit your response as soon as possible, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
412
exports/inquiry_escalation_logo_header_variant.html
Normal file
412
exports/inquiry_escalation_logo_header_variant.html
Normal file
File diff suppressed because one or more lines are too long
380
exports/inquiry_escalation_templates.html
Normal file
380
exports/inquiry_escalation_templates.html
Normal file
@ -0,0 +1,380 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Inquiry System Escalation Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 1: NEW INQUIRY NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 1: New Inquiry Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الطوارئ</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم باستلام استفسار بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">15/يوليو/2026 09:30 ص</a> من قبل المراجع برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0012</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للإطلاع عليها، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو النظر في الأمر والرد خلال 24 ساعة، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Emergency Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that an inquiry has been received on: <a href="#" style="color: #0056b3; text-decoration: underline;">15/July/2026 09:30 AM</a> by the visitor with reference number: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0012</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view it, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please look into the matter and reply within 24 hours, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 2: FIRST REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 2: First Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الأشعة</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نقدر لكم انشغالكم وحرصكم، نود
|
||||
<span style="background-color: #ffff00; padding: 0 4px; color: #000000;">تذكيركم</span>
|
||||
بالاستفسار المقدم
|
||||
<br />
|
||||
بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">10/يوليو/2026 02:15 م</a>
|
||||
<br />
|
||||
برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0008</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو منكم التكرم بالرد على هذا الاستفسار خلال الـ 24 ساعة القادمة، حرصاً على تحسين تجربة المريض وضمان معالجة الاستفسار في الوقت المناسب , للرد نأمل النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل على:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin:;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 40px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 40px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Radiology Department</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your dedication and diligence. We would like to <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">remind you</span> regarding the submitted inquiry
|
||||
<br />
|
||||
Dated: <a href="#" style="color: #0056b3; text-decoration: underline;">10/July/2026 02:15 PM</a>
|
||||
<br />
|
||||
Reference Number: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0008</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please kindly respond to this inquiry within the next 24 hours, in our commitment to enhancing patient experience and ensuring timely resolution. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin:;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 20px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 3: SECOND REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 3: Second Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الصيدلية</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نشكر لكم تعاونكم ونقدّر انشغالكم، ولكن نرجو <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">ملاحظة الوقت المتبقي للرد هو 6 ساعات</span> على الاستفسار المقدم بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">08/يوليو/2026 11:45 ص</a> برقم المرجع: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0003</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
لذا نأمل منكم التكرم بالرد والإفادة في أقرب وقت ممكن لتجنب أي تأخير إضافي في معالجة الاستفسار. وللرد، يرجى النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الاستفسار)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل معنا:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Pharmacy Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your cooperation and value your time, but please <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">note that the remaining time to respond is 6 hours</span> regarding the inquiry submitted on: <a href="#" style="color: #0056b3; text-decoration: underline;">08/July/2026 11:45 AM</a> with reference number: <a href="#" style="color: #0056b3; text-decoration: underline;">INQ-202607-0003</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Therefore, we kindly request your response and feedback as soon as possible to avoid any further delay in processing the inquiry. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Inquiry Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
380
exports/observation_escalation_templates.html
Normal file
380
exports/observation_escalation_templates.html
Normal file
@ -0,0 +1,380 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Observation System Escalation Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 1: NEW OBSERVATION NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 1: New Observation Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم التمريض</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بصدور ملاحظة بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">18/يوليو/2026 08:45 ص</a> من قبل الموظف: <a href="#" style="color: #0056b3; text-decoration: underline;">أحمد محمد</a> برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0034</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للإطلاع عليها، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو النظر في الأمر والتحقق خلال 24 ساعة، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Nursing Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that an observation has been reported on: <a href="#" style="color: #0056b3; text-decoration: underline;">18/July/2026 08:45 AM</a> by staff member: <a href="#" style="color: #0056b3; text-decoration: underline;">Ahmed Mohammed</a> with tracking code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0034</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view it, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please look into the matter and verify within 24 hours, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 2: FIRST REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 2: First Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الجراحة</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نقدر لكم انشغالكم وحرصكم، نود
|
||||
<span style="background-color: #ffff00; padding: 0 4px; color: #000000;">تذكيركم</span>
|
||||
بالملاحظة المقدمة
|
||||
<br />
|
||||
بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">14/يوليو/2026 11:20 م</a>
|
||||
<br />
|
||||
برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0028</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو منكم التكرم بالرد على هذه الملاحظة خلال الـ 24 ساعة القادمة، حرصاً على تحسين تجربة المريض وضمان معالجة الملاحظة في الوقت المناسب , للرد نأمل النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل على:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin:;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 40px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 40px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 30px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Surgery Department</h1>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your dedication and diligence. We would like to <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">remind you</span> regarding the reported observation
|
||||
<br />
|
||||
Dated: <a href="#" style="color: #0056b3; text-decoration: underline;">14/July/2026 11:20 PM</a>
|
||||
<br />
|
||||
Tracking Code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0028</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please kindly respond to this observation within the next 24 hours, in our commitment to enhancing patient experience and ensuring timely resolution. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 30px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin:;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 40px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 20px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- STAGE 3: SECOND REMINDER NOTIFICATION -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Stage 3: Second Reminder Notification
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم العناية المركزة</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نشكر لكم تعاونكم ونقدّر انشغالكم، ولكن نرجو <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">ملاحظة الوقت المتبقي للرد هو 4 ساعات</span> على الملاحظة المقدمة بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">11/يوليو/2026 03:10 م</a> برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0021</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
لذا نأمل منكم التكرم بالرد والإفادة في أقرب وقت ممكن لتجنب أي تأخير إضافي في معالجة الملاحظة. وللرد، يرجى النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد على الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، يُرجى التواصل معنا:</p>
|
||||
<p style="margin: 0 0 4px 0;">التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Critical Care Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We appreciate your cooperation and value your time, but please <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">note that the remaining time to respond is 4 hours</span> regarding the observation reported on: <a href="#" style="color: #0056b3; text-decoration: underline;">11/July/2026 03:10 PM</a> with tracking code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0021</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Therefore, we kindly request your response and feedback as soon as possible to avoid any further delay in processing the observation. To reply, please click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Response Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, please contact us via:</p>
|
||||
<p style="margin: 0 0 4px 0;">Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
172
exports/sections.csv
Normal file
172
exports/sections.csv
Normal file
@ -0,0 +1,172 @@
|
||||
Name (EN),Name (AR),Department (EN),Department (AR)
|
||||
Appointment Office,,Contact Center Department,قسم مركز الاتصال
|
||||
Operator Office,,Contact Center Department,قسم مركز الاتصال
|
||||
CCU,,Critical Care Department,قسم العناية المركزة
|
||||
ICU,,Critical Care Department,قسم العناية المركزة
|
||||
ICU Stepdown,,Critical Care Department,قسم العناية المركزة
|
||||
Dermatology,,Dermatology Department,قسم الجلدية
|
||||
Admission Office,,Emergency Administrative Department,إدارة الطوارئ
|
||||
Emergency Coordination,,Emergency Administrative Department,إدارة الطوارئ
|
||||
Emergency Management,,Emergency Administrative Department,إدارة الطوارئ
|
||||
Emergency Reception,,Emergency Administrative Department,إدارة الطوارئ
|
||||
Administrative Assistant,,Executive Administration,الإدارة التنفيذية
|
||||
Chief Administrative Officer,,Executive Administration,الإدارة التنفيذية
|
||||
Secretary,,Executive Administration,الإدارة التنفيذية
|
||||
Software Programmer,,Executive Administration,الإدارة التنفيذية
|
||||
Electrical Maintenance,,Facility Management & Maintenance Department,قسم إدارة المرافق والصيانة
|
||||
General Maintenance,,Facility Management & Maintenance Department,قسم إدارة المرافق والصيانة
|
||||
Mechanical Maintenance,,Facility Management & Maintenance Department,قسم إدارة المرافق والصيانة
|
||||
Safety,,Facility Management & Maintenance Department,قسم إدارة المرافق والصيانة
|
||||
Water Treatment,,Facility Management & Maintenance Department,قسم إدارة المرافق والصيانة
|
||||
Financial Collection & Claims,,Financial Collection & Claims Department,قسم المالية والتحصيل والمطالبات
|
||||
General,,Information Technology Department,قسم تقنية المعلومات
|
||||
Service Desk,,Information Technology Department,قسم تقنية المعلومات
|
||||
Software and Applications,,Information Technology Department,قسم تقنية المعلومات
|
||||
Social Worker,,Inpatient Department,قسم التنويم
|
||||
AKU,,Internal Medicine Department,قسم الباطنية
|
||||
Cardiology,,Internal Medicine Department,قسم الباطنية
|
||||
Endocrinology,,Internal Medicine Department,قسم الباطنية
|
||||
Endoscopy,,Internal Medicine Department,قسم الباطنية
|
||||
Gastroenterology,,Internal Medicine Department,قسم الباطنية
|
||||
Infectious Disease,,Internal Medicine Department,قسم الباطنية
|
||||
Internal Medicine,,Internal Medicine Department,قسم الباطنية
|
||||
Long-Term Acute Care Unit (LTACU),,Internal Medicine Department,قسم الباطنية
|
||||
Medical Ward,,Internal Medicine Department,قسم الباطنية
|
||||
Nephrology,,Internal Medicine Department,قسم الباطنية
|
||||
Neurology,,Internal Medicine Department,قسم الباطنية
|
||||
Psychiatry,,Internal Medicine Department,قسم الباطنية
|
||||
Pulmonology,,Internal Medicine Department,قسم الباطنية
|
||||
Rheumatology,,Internal Medicine Department,قسم الباطنية
|
||||
Blood Donation & Blood Bank,,Laboratory Department,قسم المختبر
|
||||
Clinical Chemistry,,Laboratory Department,قسم المختبر
|
||||
General,,Laboratory Department,قسم المختبر
|
||||
Hematology,,Laboratory Department,قسم المختبر
|
||||
Histopathology,,Laboratory Department,قسم المختبر
|
||||
Microbiology,,Laboratory Department,قسم المختبر
|
||||
Parasitology,,Laboratory Department,قسم المختبر
|
||||
Phlepotomy,,Laboratory Department,قسم المختبر
|
||||
Receiving ِArea,,Laboratory Department,قسم المختبر
|
||||
Send Out,,Laboratory Department,قسم المختبر
|
||||
Serology & Molecular,,Laboratory Department,قسم المختبر
|
||||
Special Chemistry,,Laboratory Department,قسم المختبر
|
||||
General,,Medical Administration,الإدارة الطبية
|
||||
Medical Director,,Medical Administration,الإدارة الطبية
|
||||
Secretary,,Medical Administration,الإدارة الطبية
|
||||
Anesthesia Technology,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
CSSD,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Cardiac Physiology Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Clinical Nutrition,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Dental Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Director of Ancillary Medical Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Emergency Medical Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
General,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Health Education,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Neurophysiology Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Optometrist Services,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Physical Therapy,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Respiratory Therapy,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Secretary,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Swallowing Speech and Hearing Unit,,Medical Ancillary Services Department,قسم الخدمات الطبية المساندة
|
||||
Emergency,,Medical Approvals Department,قسم الموافقات الطبية
|
||||
General,,Medical Approvals Department,قسم الموافقات الطبية
|
||||
Inpatient,,Medical Approvals Department,قسم الموافقات الطبية
|
||||
Outpatient,,Medical Approvals Department,قسم الموافقات الطبية
|
||||
AKU,,Nursing Department,قسم التمريض
|
||||
Anesthesia,,Nursing Department,قسم التمريض
|
||||
Cathlab,,Nursing Department,قسم التمريض
|
||||
Emergency,,Nursing Department,قسم التمريض
|
||||
Endoscopy,,Nursing Department,قسم التمريض
|
||||
ICU/CCU,,Nursing Department,قسم التمريض
|
||||
ICU/CCU Stepdown,,Nursing Department,قسم التمريض
|
||||
L&D,,Nursing Department,قسم التمريض
|
||||
LTACU,,Nursing Department,قسم التمريض
|
||||
Main OR,,Nursing Department,قسم التمريض
|
||||
Management,,Nursing Department,قسم التمريض
|
||||
Medical Ward,,Nursing Department,قسم التمريض
|
||||
NICU,,Nursing Department,قسم التمريض
|
||||
NICU ISOLATOIN,,Nursing Department,قسم التمريض
|
||||
Nursery,,Nursing Department,قسم التمريض
|
||||
OB/OR,,Nursing Department,قسم التمريض
|
||||
OPD 5,,Nursing Department,قسم التمريض
|
||||
OPD 6,,Nursing Department,قسم التمريض
|
||||
OPD 7,,Nursing Department,قسم التمريض
|
||||
OPD1,,Nursing Department,قسم التمريض
|
||||
OPD2,,Nursing Department,قسم التمريض
|
||||
OPD3,,Nursing Department,قسم التمريض
|
||||
OPD4 Dental,,Nursing Department,قسم التمريض
|
||||
OPD4 Ophtha,,Nursing Department,قسم التمريض
|
||||
Obgyne Ward,,Nursing Department,قسم التمريض
|
||||
Oncology,,Nursing Department,قسم التمريض
|
||||
Orthopedic Cast Technician,,Nursing Department,قسم التمريض
|
||||
Outpatient,,Nursing Department,قسم التمريض
|
||||
PICU,,Nursing Department,قسم التمريض
|
||||
PICU Stepdown,,Nursing Department,قسم التمريض
|
||||
Pediatric Ward,,Nursing Department,قسم التمريض
|
||||
Recovery,,Nursing Department,قسم التمريض
|
||||
Surgical Ward,,Nursing Department,قسم التمريض
|
||||
Hematology,,ONCOLOGY Department,قسم الأورام
|
||||
Clinics,,Obstetrics & Gynecology Department,قسم النساء والولادة
|
||||
Labor & Delivery (L&D) / Obstetrics Operating Rooms,,Obstetrics & Gynecology Department,قسم النساء والولادة
|
||||
Wards,,Obstetrics & Gynecology Department,قسم النساء والولادة
|
||||
Coordination,,Outpatient Department,قسم العيادات الخارجية
|
||||
Management,,Outpatient Department,قسم العيادات الخارجية
|
||||
Medical Reports,,Outpatient Department,قسم العيادات الخارجية
|
||||
Medical Secretarial,,Outpatient Department,قسم العيادات الخارجية
|
||||
Outpatient Reception,,Outpatient Department,قسم العيادات الخارجية
|
||||
Patient Affairs Director,,Patient Affairs Department,قسم علاقات المرضى
|
||||
Secretary,,Patient Affairs Department,قسم علاقات المرضى
|
||||
Patient Experience Director,,Patient Experience Department,قسم تجربة المريض
|
||||
Patient Experience Staff,,Patient Experience Department,قسم تجربة المريض
|
||||
Circumcision,,Pediatric Department,قسم الأطفال
|
||||
General Pediatrics,,Pediatric Department,قسم الأطفال
|
||||
NICU,,Pediatric Department,قسم الأطفال
|
||||
NICU ISOLATOIN,,Pediatric Department,قسم الأطفال
|
||||
NURSERY,,Pediatric Department,قسم الأطفال
|
||||
PICU,,Pediatric Department,قسم الأطفال
|
||||
PICU STEPDOWN,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Cardiology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Endocrinology & Diabetes,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Gastroenterology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric General Surgery,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Hematology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Nephrology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Neurology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Neurosurgery,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Orthopedic Surgery,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Otolaryngology (ENT),,Pediatric Department,قسم الأطفال
|
||||
Pediatric Pulmonology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Urology,,Pediatric Department,قسم الأطفال
|
||||
Pediatric Ward,,Pediatric Department,قسم الأطفال
|
||||
6th Floor Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Central Outpatient Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
ER Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
General,,Pharmacy Department,قسم الصيدلية
|
||||
IV Admixture Unit,,Pharmacy Department,قسم الصيدلية
|
||||
Inpatient Oncology Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Inpatient Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Narcotic Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Outpatient Oncology Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Pediatric Pharmacy,,Pharmacy Department,قسم الصيدلية
|
||||
Pharmacy Warehouse,,Pharmacy Department,قسم الصيدلية
|
||||
Bone Mineral Density (BMD),,Radiology Department,قسم الأشعة
|
||||
CT,,Radiology Department,قسم الأشعة
|
||||
Fluoroscopy,,Radiology Department,قسم الأشعة
|
||||
General,,Radiology Department,قسم الأشعة
|
||||
Interventional Radiology (IR),,Radiology Department,قسم الأشعة
|
||||
MRI,,Radiology Department,قسم الأشعة
|
||||
Mamography,,Radiology Department,قسم الأشعة
|
||||
Ultrasound,,Radiology Department,قسم الأشعة
|
||||
X-Ray,,Radiology Department,قسم الأشعة
|
||||
Camera Room,,Security Department,قسم الأمن
|
||||
Security Guards,,Security Department,قسم الأمن
|
||||
Bariatric,,Surgery Department,قسم الجراحة
|
||||
ENT,,Surgery Department,قسم الجراحة
|
||||
General Surgery,,Surgery Department,قسم الجراحة
|
||||
Neurosurgery,,Surgery Department,قسم الجراحة
|
||||
Orthopedic Surgery,,Surgery Department,قسم الجراحة
|
||||
Plastic Surgery,,Surgery Department,قسم الجراحة
|
||||
Spine Surgery,,Surgery Department,قسم الجراحة
|
||||
Surgical Ward,,Surgery Department,قسم الجراحة
|
||||
Thoracic Surgery,,Surgery Department,قسم الجراحة
|
||||
Urology,,Surgery Department,قسم الجراحة
|
||||
Vscular Surgery,,Surgery Department,قسم الجراحة
|
||||
|
384
exports/status_changes.html
Normal file
384
exports/status_changes.html
Normal file
@ -0,0 +1,384 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Status Change Notification Templates</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f3f4f6; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="background-color: #f3f4f6; padding: 40px 10px;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 1: OBSERVATION ASSIGNED -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Observation Assigned
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / أحمد محمد</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بتعيين ملاحظة لكم للمراجعة واتخاذ الإجراء اللاحق، بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">18/يوليو/2026 09:00 ص</a> برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0034</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للإطلاع على التفاصيل، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو مراجعة التفاصيل وتحديث الحالة خلال 24 ساعة، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج التحديث)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Ahmed Mohammed</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that an observation has been assigned to you for review and action, dated: <a href="#" style="color: #0056b3; text-decoration: underline;">18/July/2026 09:00 AM</a> with tracking code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0034</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the details, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please review the details and update the status within 24 hours, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Update Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 2: OBSERVATION RESOLVED -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Observation Resolved
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); margin-bottom: 60px;">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى السيد / خالد العنزي</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود إبلاغكم بأنه قد تم <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">حل</span> الملاحظة المسندة إليكم بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">19/يوليو/2026 11:30 ص</a> برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0041</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على تفاصيل الحل، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو مراجعة تفاصيل الحل والإفادة في حال وجود أي ملاحظات، وللرد إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الرد)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Mr. Khalid Al-Anazi</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to inform you that the observation assigned to you has been <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">resolved</span>, dated: <a href="#" style="color: #0056b3; text-decoration: underline;">19/July/2026 11:30 AM</a> with tracking code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202607-0041</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view the resolution details, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please review the resolution details and provide feedback if any, and to reply, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Reply Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- ========================================================================== -->
|
||||
<!-- CARD 3: OBSERVATION MONTHLY FOLLOW-UP -->
|
||||
<!-- ========================================================================== -->
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; margin-bottom: 15px;">
|
||||
<tr>
|
||||
<td align="center" style="font-size: 18px; font-weight: bold; color: #4b5563; text-transform: uppercase; letter-spacing: 1px;">
|
||||
▼ Observation Monthly Follow-Up
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px; background-color: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);">
|
||||
<tr>
|
||||
<td style="padding: 40px 30px;">
|
||||
|
||||
<!-- ARABIC SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="rtl" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">إلى قسم الجودة</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">تحية طيبة</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نود تذكيركم بمتابعة <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">المراجعة الشهرية</span> للملاحظة التي تم حلها بتاريخ: <a href="#" style="color: #0056b3; text-decoration: underline;">19/يونيو/2026</a> برمز التتبع: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202606-0019</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
للاطلاع على التفاصيل والتحقق من استمرار فعالية الإجراءات، يمكنكم النقر على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج الملاحظة)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
نرجو تحديث الملاحظة بملاحظات المتابعة، وللإفادة إضغط على
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(نموذج التحديث)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">للإستفسارات أو لمزيد من المعلومات، على التحويلة: 1379</p>
|
||||
<p style="margin: 0;">رقم التواصل: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">رابط</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 30px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
نحن نقدر التزامكم، مع تحيات إدارة قسم علاقات المرضى وتجربة المريض
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: 0; border-top: 1px dashed #d1d5db; margin: 20px 0 35px 0;" />
|
||||
|
||||
<!-- ENGLISH SECTION -->
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" dir="ltr" style="text-align: center;">
|
||||
<tr>
|
||||
<td>
|
||||
<h1 style="margin: 0 0 10px 0; font-size: 24px; font-weight: bold; color: #000000;">To: Quality Department</h1>
|
||||
<h2 style="margin: 0 0 25px 0; font-size: 20px; font-weight: 600; color: #374151;">Greetings,</h2>
|
||||
|
||||
<p style="margin: 0 0 15px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
We would like to remind you about the <span style="background-color: #ffff00; padding: 0 4px; color: #000000;">monthly follow-up review</span> for the observation resolved on: <a href="#" style="color: #0056b3; text-decoration: underline;">19/June/2026</a> with tracking code: <a href="#" style="color: #0056b3; text-decoration: underline;">OBS-202606-0019</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
To view details and verify the corrective actions remain effective, you can click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Observation Form)</a>
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
Please update the observation with follow-up notes, and to provide feedback, click on:
|
||||
</p>
|
||||
|
||||
<p style="margin: 0 0 25px 0;">
|
||||
<a href="#" style="color: #dc2626; font-weight: bold; font-size: 18px; text-decoration: underline;">(Update Form)</a>
|
||||
</p>
|
||||
|
||||
<div style="margin: 0 0 25px 0; font-size: 16px; line-height: 1.6; color: #111827; font-weight: 600;">
|
||||
<p style="margin: 0 0 4px 0;">For inquiries or more information, via Extension: 1379</p>
|
||||
<p style="margin: 0;">Contact Number: <a href="tel:0537695000" style="color: #0056b3; text-decoration: underline;">0537695000</a></p>
|
||||
</div>
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="margin: 0 auto 35px auto;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="#008cc1" style="border-radius: 2px;">
|
||||
<a href="#" target="_blank" style="padding: 12px 30px; display: inline-block; font-size: 16px; font-weight: bold; color: #004b69; text-decoration: none;">Link</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="margin: 0 0 10px 0; font-size: 16px; color: #1f2937; font-weight: 600;">
|
||||
We appreciate your commitment.<br />Regards, Patient Relations and Experience Management
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb" style="padding: 20px 30px; border-top: 1px solid #f3f4f6; border-radius: 0 0 4px 4px; text-align: center; font-size: 12px; color: #6b7280; font-weight: 500;">
|
||||
جميع الحقوق محفوظة © Al Hammadi Hospitals Group
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user