Nessuna descrizione

case.html 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. {% extends "layouts/default_ext.html" %} {% block title %} Case summary {% endblock title %}
  2. {% block stylesheets %}
  3. {% include 'includes/header_case.html' %}
  4. <link rel="stylesheet" href="/static/assets/css/select2.css">
  5. <link rel="stylesheet" href="/static/assets/css/bootstrap-multiselect.min.css">
  6. <link rel="stylesheet" href="/static/assets/css/bootstrap-select.min.css">
  7. {% endblock stylesheets %}
  8. {% block content %}
  9. {% if current_user.is_authenticated %}
  10. {% include 'includes/navigation_ext.html' %}
  11. {% include 'includes/sidenav.html' %}
  12. <div class="main-panel">
  13. <div class="content">
  14. {% if case.close_date %}
  15. <div class="panel-header bg-close-gradient">
  16. {% else %}
  17. <div class="panel-header bg-info-gradient">
  18. {% endif %}
  19. <div class="page-inner py-5">
  20. <div class="d-flex align-items-left align-items-md-center flex-column flex-md-row mt--3">
  21. <div class="col">
  22. <div class="row">
  23. <div class="col">
  24. <h2 class="text-white pb-2 fw-bold case-name"> {{ case.name|unquote }}
  25. </h2>
  26. </div>
  27. </div>
  28. <div class="row">
  29. <div class="col">
  30. <h5 class="text-white op-7 mb-1"><b>Open on</b> {{ case.open_date }} by {{ case.user.name }}</h5>
  31. <h5 class="text-white op-7 mb-3"><b>Owned by</b> {{ case.owner.name }}</h5>
  32. {% if case.close_date %}
  33. <h5 class="text-warning mb-1">Closed on {{ case.close_date }}</h5>
  34. {% endif %}
  35. </div>
  36. <div class="col mt-auto">
  37. <div class="row">
  38. {% if case.severity %}<span onclick="case_detail('{{ case.case_id }}', true);" class="btn btn-rounded badge-pill hidden-caret btn-sm ml-2 mb-2 ml-auto {% if case.severity.severity_id > 4 %} badge-danger {% elif case.severity.severity_id == 4 %} bg-warning-gradient text-dark {% else %} btn-light {% endif %} ml-2" title="Case severity"><i class="fa-solid fa-bolt mr-1"></i> {{ case.severity.severity_name }}</span>{% endif %}
  39. <span title="Case outcome" class="float-right btn btn-rounded badge-pill hidden-caret btn-sm ml-2 mb-2 {% if case.status_id == 1%}badge-success{% elif case.status_id == 2 %}badge-danger{% else %}btn-light{% endif %}"
  40. onclick="case_detail('{{ case.case_id }}', true);"
  41. ><i class="fa-solid fa-group-arrows-rotate mr-2"></i>{{ case.status_name }}</span>
  42. <span id="case-kpi-badge" class="ml-2 mb-2 d-inline-flex align-items-center" style="gap:4px;color:#fff;opacity:.9"></span>
  43. </div>
  44. <div class="row">
  45. <div class="ml-auto">
  46. <div class="row">
  47. <h5 class="text-white op-7 mb-2 float-right mr-4"><b>Customer</b> : {{ case.client.name }}</h5>
  48. </div>
  49. <div class="row">
  50. {% if case.soc_id %} <h5 class="text-white op-7 mb-2 mr-4"><b>SOC ID :</b> {{ case.soc_id }}</h5> {% endif %}
  51. </div>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. </div>
  58. <div class="row mt-2 mb--2">
  59. <div class="ml-2 col">
  60. <div class="row ml-1">
  61. {% if case.state %}<h5 title="Case state" onclick="case_detail('{{ case.case_id }}', true);" style="cursor:pointer;"><span class="btn-rounded badge-pill hidden-caret btn-sm btn-light" title="Case state"><i class="fa-solid fa-business-time mr-1"></i> {{ case.state.state_name }}</span></h5>{% endif %}
  62. {% if case.classification %}<h5 title="Classification" onclick="case_detail('{{ case.case_id }}', true);" style="cursor:pointer;"><span class="btn-rounded badge-pill hidden-caret btn-sm btn-light ml-2"><i class="fa-solid fa-shield-virus mr-1"></i>{{ case.classification.name_expanded }}</span></h5>{% endif %}
  63. {% if case.alerts| length > 0 %}<h5 title="Alerts"><a class="btn-rounded badge-pill hidden-caret btn-sm btn-dark ml-2 bg-warning-gradient text-dark" href="/alerts?cid={{ case.case_id }}&sort=desc&case_id={{ case.case_id }}" target="_blank" rel="noopener"><i class="fa-solid fa-bell mr-1"></i> {{ case.alerts| length }} related alerts</a></h5>{% endif %}
  64. {% if case.review_status.status_name == "Reviewed" %}
  65. <h5 title="Reviewed"> <a class="text-white btn-rounded badge-pill hidden-caret btn-sm ml-2 badge-success"><i class="fa-regular fa-circle-check mr-2"></i>Case reviewed by {% if case.reviewer.id == current_user.id %} you {% else %} {{ case.reviewer.name }} {% endif %}</a></h5>
  66. {% endif %}
  67. </div>
  68. </div>
  69. <div class="col mr-1">
  70. {% if case.case_tags %}
  71. {% for tag in case.case_tags %}
  72. <span class="badge badge-pill badge-light ml-1 pull-right"><i class="fa fa-tag mr-1"></i> {{ tag }}</span>
  73. {% endfor %}
  74. {% endif %}
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <div class="page-inner mt--5">
  80. <div class="row row-card-no-pd" style="padding-top: 0px;padding-bottom: 3px;">
  81. {% include 'case-nav_landing.html' %}
  82. </div>
  83. <div id="caseReviewState" data-review-state="{{ case.review_status.status_name }}" data-reviewer-id="{{ case.reviewer_id }}" data-reviewer-name="{{ case.reviewer.name }}" style="display: none;"></div>
  84. {% if case.reviewer_id == current_user.id and case.review_status.status_name != "Reviewed" and case.review_status.status_name != "Not reviewed" %}
  85. <div class="row row-card-no-pd review-card mt--3 mb--3 bg-warning-gradient" style="display: none;">
  86. <div class="col-md-12">
  87. <h4 class="font-weight-bold"><i class="fa-solid fa-triangle-exclamation text-danger ml-2 mr-2"></i>Review requested
  88. <button class="btn btn-sm float-right btn-dark mr-3 mt-2 btn-start-review">Start review</button>
  89. <button class="btn btn-sm float-right btn-success mr-3 mt-2 btn-confirm-review" style="display:none">Confirm review</button>
  90. <button class="btn btn-sm float-right btn-light mr-3 mt-2 btn-cancel-review" style="display:none">Cancel review</button></h4>
  91. <span class="ml-2" id="reviewSubtitle">You have been requested to review this case.</span>
  92. </div>
  93. </div>
  94. {% elif case.review_status.status_name == "Review in progress" %}
  95. <div class="row row-card-no-pd mt--3 mb--3 bg-warning-gradient">
  96. <div class="col-md-12">
  97. <h4 class="font-weight-bold mt-1"><i class="fa-solid fa-list-check ml-2 mr-2"></i>Review by {{ case.reviewer.name }} in progress</h4>
  98. </div>
  99. </div>
  100. {% elif case.review_status.status_name == "Pending review" %}
  101. <div class="row row-card-no-pd mt--3 mb--3 bg-warning-gradient">
  102. <div class="col-md-12">
  103. <h4 class="font-weight-bold mt-1"><i class="fa-solid fa-triangle-exclamation text-danger ml-2 mr-2"></i>Review by {{ case.reviewer.name }} pending</h4>
  104. </div>
  105. </div>
  106. {% endif %}
  107. <div class="row row-card-no-pd">
  108. <div class="col-md-12">
  109. <div class="card mb-4" id="rescard1">
  110. <div class="card-header">
  111. <div class="row">
  112. {{ form.hidden_tag() }}
  113. <a href="#case_summary_card" class="d-block nav-link mr-auto" data-toggle="collapse" aria-expanded="true" aria-controls="case_summary_card">
  114. <h4 class="m-0 font-weight-bold">Case summary {{ "(Syncing with DB )" if case.id }}</h4>
  115. </a>
  116. <div class="mr-0 float-right">
  117. <small id="content_typing" class="mr-3 mt-1"></small>
  118. <small id="content_last_saved_by" class="mr-3 mt-1"></small>
  119. <span id="last_saved" class="badge mr-3 ml-2"></span>
  120. <small id="content_last_sync"></small>
  121. <button class="btn btn-sm mr-2 ml-3" onclick="edit_case_summary();" id="sum_edit_btn" >Edit</button>
  122. <button type="button" id="sum_refresh_btn" class="btn btn-sm btn-outline-default mr-3" onclick="sync_editor();">
  123. Refresh
  124. </button>
  125. </div>
  126. </div>
  127. </div>
  128. <div class="collapsed" id="case_summary_card">
  129. <div class="card-body">
  130. <div class="row mb-1">
  131. <div class="col" id="summary_edition_btn" style="display:none;">
  132. <div class="btn btn-sm btn-light mr-1 " title="CTRL-B" onclick="editor.insertSnippet('**${1:$SELECTION}**');editor.focus();"><i class="fa-solid fa-bold"></i></div>
  133. <div class="btn btn-sm btn-light mr-1" title="CTRL-I" onclick="editor.insertSnippet('*${1:$SELECTION}*');editor.focus();"><i class="fa-solid fa-italic"></i></div>
  134. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-1" onclick="editor.insertSnippet('# ${1:$SELECTION}');editor.focus();">H1</div>
  135. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-2" onclick="editor.insertSnippet('## ${1:$SELECTION}');editor.focus();">H2</div>
  136. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-3" onclick="editor.insertSnippet('### ${1:$SELECTION}');editor.focus();">H3</div>
  137. <div class="btn btn-sm btn-light mr-1" title="CTRL-SHIFT-4" onclick="editor.insertSnippet('#### ${1:$SELECTION}');editor.focus();">H4</div>
  138. <div class="btn btn-sm btn-light mr-1" title="Insert code" onclick="editor.insertSnippet('```${1:$SELECTION}```');editor.focus();"><i class="fa-solid fa-code"></i></div>
  139. <div class="btn btn-sm btn-light mr-1" title="Insert link" onclick="editor.insertSnippet('[${1:$SELECTION}](url)');editor.focus();"><i class="fa-solid fa-link"></i></div>
  140. <div class="btn btn-sm btn-light mr-1" title="Insert table" onclick="editor.insertSnippet('|\t|\t|\t|\n|--|--|--|\n|\t|\t|\t|\n|\t|\t|\t|');editor.focus();"><i class="fa-solid fa-table"></i></div>
  141. <div class="btn btn-sm btn-light mr-1" title="Insert bullet list" onclick="editor.insertSnippet('\n- \n- \n- ');editor.focus();"><i class="fa-solid fa-list"></i></div>
  142. <div class="btn btn-sm btn-light mr-1" title="Insert numbered list" onclick="editor.insertSnippet('\n1. a \n2. b \n3. c ');editor.focus();"><i class="fa-solid fa-list-ol"></i></div>
  143. </div>
  144. </div>
  145. <div class="row">
  146. <div class="col-md-6" id="container_editor_summary">
  147. <div style="display: none" id="fetched_crc"></div>
  148. <div id="editor_summary" contenteditable="true" spellcheck="true" data-theme="{% if current_user.in_dark_mode %}dark{% else %}light{% endif %}"></div>
  149. <textarea id="case_summary" rows="10" cols="82" style="display: none"></textarea>
  150. </div>
  151. <div class="col-md-6" id="ctrd_casesum">
  152. <div id="targetDiv"></div>
  153. </div>
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. </div>
  159. </div>
  160. </div>
  161. <div class="modal " tabindex="-1" role="dialog" id="modal_select_report" data-backdrop="true">
  162. <div class="modal-lg modal-dialog" role="document">
  163. <div class="modal-content">
  164. <div class="modal-header">
  165. <h5>Select report template</h5>
  166. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
  167. aria-hidden="true">&times;</span></button>
  168. </div>
  169. <div class="modal-body">
  170. {% if reports| length == 0 %}
  171. <div class="alert alert-warning" role="alert">
  172. <h4 class="alert-heading">No report template found</h4>
  173. <p>Report templates are configured in <a href="/manage/templates?cid={{case.case_id}}">the management section</a>.</p>
  174. </div>
  175. {% else %}
  176. <div class="col">
  177. <p>Since IRIS v2.0.0, the report generation supports images. Integration of images might fail depending on the situation.<br/><code>Safe Mode</code> can be used to generate the report without them.</p>
  178. </div>
  179. <select class="selectpicker form-control bg-outline-success dropdown-submenu" data-show-subtext="true" data-live-search="true" id="select_report">
  180. {% for report in reports %}
  181. <option data-toggle="tooltip" value="{{ report[0] }}" data-subtext="{{ report[3] }}">{{ report[1] }} ({{ report[2].capitalize() }})</option>
  182. {% endfor %}
  183. </select>
  184. </div>
  185. <div class="modal-footer">
  186. <a href="#" class="btn btn-light float-left mt-2 mr-auto" onclick="gen_report(true);">
  187. <span class="btn-label">
  188. <i class="fa fa-file-download"></i>
  189. </span>
  190. Generate in Safe Mode
  191. </a>
  192. <a href="#" class="btn btn-light float-right mt-2 ml-2" onclick="gen_report(false);">
  193. <span class="btn-label">
  194. <i class="fa fa-file-download"></i>
  195. </span>
  196. Generate
  197. </a>
  198. {% endif %}
  199. </div>
  200. </div><!-- /.modal-content -->
  201. </div><!-- /.modal-dialog -->
  202. </div>
  203. <div class="modal " tabindex="-1" role="dialog" id="modal_select_report_act" data-backdrop="true">
  204. <div class="modal-lg modal-dialog" role="document">
  205. <div class="modal-content">
  206. <div class="modal-header">
  207. <h5>Select activity report template</h5>
  208. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
  209. aria-hidden="true">&times;</span></button>
  210. </div>
  211. <div class="modal-body">
  212. {% if reports| length == 0 %}
  213. <div class="alert alert-warning" role="alert">
  214. <h4 class="alert-heading">No report template found</h4>
  215. <p>Report templates are configured in <a href="/manage/templates?cid={{case.case_id}}">the management section</a>.</p>
  216. </div>
  217. {% else %}
  218. <div class="col">
  219. <p>Since IRIS v2.0.0, the report generation supports images. Integration of images might fail depending on the situation.<br/><code>Safe Mode</code> can be used to generate the report without them.</p>
  220. </div>
  221. <select class="selectpicker form-control bg-outline-success dropdown-submenu mb-2" data-show-subtext="true" data-live-search="true" id="select_report_act">
  222. {% for report in reports_act %}
  223. <option data-toggle="tooltip" value="{{ report[0] }}" data-subtext="{{ report[3] }}">{{ report[1] }} ({{ report[2].capitalize() }})</option>
  224. {% endfor %}
  225. </select>
  226. </div>
  227. <div class="modal-footer">
  228. <a href="#" class="btn btn-light float-left mt-2 mr-auto" onclick="gen_act_report(true);">
  229. <span class="btn-label">
  230. <i class="fa fa-file-download"></i>
  231. </span>
  232. Generate in Safe Mode
  233. </a>
  234. <a href="#" class="btn btn-light float-right mt-2 ml-2" onclick="gen_act_report(false);">
  235. <span class="btn-label">
  236. <i class="fa fa-file-download"></i>
  237. </span>
  238. Generate
  239. </a>
  240. {% endif %}
  241. </div>
  242. </div><!-- /.modal-content -->
  243. </div><!-- /.modal-dialog -->
  244. </div>
  245. <div class="modal " tabindex="-1" role="dialog" id="modal_choose_reviewer" data-backdrop="true">
  246. <div class="modal-lg modal-dialog" role="document">
  247. <div class="modal-content">
  248. <form method="post" action="" id="form_choose_reviewer">
  249. <div class="modal-header">
  250. <h5>Choose reviewer</h5>
  251. <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
  252. aria-hidden="true">&times;</span></button>
  253. </div>
  254. <div class="modal-body">
  255. <div class="row mb-2">
  256. <div class="col-12">
  257. <div class="form-group">
  258. <select class="selectpicker form-control" data-dropup-auto="false" data-live-search="true" id="reviewer_id">
  259. </select>
  260. </div>
  261. </div>
  262. </div>
  263. <div class="row mt-4">
  264. <div class="col-12 d-flex">
  265. <button type="button" class="btn btn-default mr-auto" data-dismiss="modal">Cancel</button>
  266. <button type="button" class="btn btn-outline-success ml-auto" id="submit_set_reviewer">Request</button>
  267. </div>
  268. </div>
  269. </div>
  270. </form>
  271. </div><!-- /.modal-content -->
  272. </div><!-- /.modal-dialog -->
  273. </div>
  274. <div class="modal" tabindex="-1" role="dialog" id="modal_case_detail" data-backdrop="true">
  275. <div class="modal-xl modal-dialog" role="document">
  276. <div class="modal-content" id="info_case_modal_content">
  277. </div><!-- /.modal-content -->
  278. </div><!-- /.modal-dialog -->
  279. </div>
  280. <div class="modal bg-shadow-gradient" tabindex="-1" role="dialog" id="modal_ac_additional" data-backdrop="true">
  281. </div>
  282. <div class="modal bg-shadow-gradient" tabindex="-1" role="dialog" id="modal_case_review" data-backdrop="static" data-keyboard="false">
  283. <div class="modal-dialog modal-lg" role="document">
  284. <div class="modal-content">
  285. <div class="modal-header">
  286. <h5 class="modal-title">Case Review</h5>
  287. <button type="button" class="close" data-dismiss="modal" aria-label="Close">
  288. <span aria-hidden="true">&times;</span>
  289. </button>
  290. </div>
  291. <div class="modal-body">
  292. <div class="row">
  293. <div class="col-12">
  294. <p>Do you confirm that the case has been reviewed?</p>
  295. </div>
  296. </div>
  297. <div class="row">
  298. <div class="col-12">
  299. <button type="button" class="btn btn-danger float-left" data-dismiss="modal">Cancel</button>
  300. <button type="button" class="btn btn-success float-right" id="confirmReview">Confirm Review</button>
  301. </div>
  302. </div>
  303. </div>
  304. </div>
  305. </div>
  306. </div>
  307. </div>
  308. </div>
  309. </div>
  310. {% include 'includes/footer.html' %}
  311. {% endif %} {% endblock content %} {% block javascripts %}
  312. <script src="/static/assets/js/plugin/ace/src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
  313. <script src="/static/assets/js/plugin/ace/src-noconflict/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
  314. <script src="/static/assets/js/core/socket.io.js"></script>
  315. <script src="/static/assets/js/plugin/select/select2.js"></script>
  316. <script src="/static/assets/js/plugin/showdown/showdown.min.js"></script>
  317. <script src="/static/assets/js/iris/datatablesUtils.js"></script>
  318. <script src="/static/assets/js/iris/case.js"></script>
  319. <script src="/static/assets/js/iris/manage.cases.common.js"></script>
  320. <script src="/static/assets/js/iris/case.common.js"></script>
  321. <script src="/static/assets/js/iris/case.summary.js"></script>
  322. <script src="/static/assets/js/plugin/select/bootstrap-select.min.js"></script>
  323. <script src="/static/assets/js/plugin/select/bootstrap-multiselect.min.js"></script>
  324. <script>
  325. $('#modal_select_report').selectpicker();
  326. load_menu_mod_options_modal([{{case.case_id}}], 'case', $("#case_modal_quick_actions"));
  327. (async function loadCaseKpi() {
  328. try {
  329. const res = await fetch('/kpi-dashboard/api/cases/{{ case.case_id }}');
  330. if (!res.ok) return;
  331. const json = await res.json();
  332. const kpi = json.data?.case?.kpi;
  333. if (!kpi) return;
  334. const segs = (kpi.segments || []).map(s =>
  335. `<div style="width:18px;height:8px;border-radius:3px;background:${s.active ? s.color : 'rgba(255,255,255,.25)'}" title="${s.label}"></div>`
  336. ).join('');
  337. document.getElementById('case-kpi-badge').innerHTML =
  338. `<b><i class="fa-solid fa-gauge-high"></i></b>
  339. <span style="display:flex;gap:3px">${segs}</span>
  340. <small>${kpi.status}</small>`;
  341. } catch(e) {}
  342. })();
  343. </script>
  344. {% endblock javascripts %}