wip
This commit is contained in:
parent
1bec941b8c
commit
98d4e4e4b7
@ -16,6 +16,7 @@ enum Permission: string
|
||||
case ReportEdit = 'report_edit';
|
||||
case ReportDictate = 'report_dictate';
|
||||
case ReportDownload = 'report_download';
|
||||
case ReportApprove = 'report_approve';
|
||||
case StudyNotesCreate = 'study_notes_create';
|
||||
case StudyNotesView = 'study_notes_view';
|
||||
case AttachmentUpload = 'attachment_upload';
|
||||
|
@ -3,8 +3,11 @@
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Domain\ACL\Permission;
|
||||
use App\Domain\Report\ReportStatus;
|
||||
use App\Http\Controllers\HashidControllerBase;
|
||||
use App\Http\Requests\CreateReportRequest;
|
||||
use App\Models\Study;
|
||||
use App\Models\StudyReport;
|
||||
|
||||
class ReportController extends HashidControllerBase
|
||||
{
|
||||
@ -12,7 +15,7 @@ public function popup()
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportDownload]), 403);
|
||||
$this->decodeKeys();
|
||||
$study = Study::with('reports')->findOrFail($this->key);
|
||||
$study = Study::with(['reports.radiologist', 'readingPhysician'])->findOrFail($this->key);
|
||||
if (me()->isRadiologist()) {
|
||||
abort_unless($study->isAssignedTo(), 403);
|
||||
}
|
||||
@ -20,4 +23,39 @@ public function popup()
|
||||
|
||||
return view('staff.reports.popup', compact('study', 'reports'));
|
||||
}
|
||||
|
||||
public function save(CreateReportRequest $request)
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
|
||||
$report = StudyReport::make([
|
||||
'study_id' => $study->id,
|
||||
'institute_id' => $study->institute_id,
|
||||
'facility_id' => $study->facility_id,
|
||||
'report_status' => ReportStatus::Preliminary->value,
|
||||
'read_by_id' => me()->id,
|
||||
]);
|
||||
$report->setContent(request('content'));
|
||||
$report->save();
|
||||
|
||||
return redirect()->back()->with('success', 'Report saved successfully.');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
abort_unless(me()->may([Permission::ReportEdit, Permission::ReportDictate, Permission::ReportApprove]), 403);
|
||||
$this->decodeKeys();
|
||||
$study = Study::findOrFail($this->key);
|
||||
if ($study->report_status >= ReportStatus::Finalized) {
|
||||
return redirect()->back()->with('error', 'Report is already approved.');
|
||||
}
|
||||
$report = StudyReport::where('study_id', $study->id)
|
||||
->where('report_status', ReportStatus::Preliminary->value)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
return view('staff.reports.create', compact('study', 'report'));
|
||||
}
|
||||
}
|
||||
|
23
app/Http/Requests/CreateReportRequest.php
Normal file
23
app/Http/Requests/CreateReportRequest.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Study;
|
||||
use App\Rules\ExistsByHash;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateReportRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'hashid' => ['required', new ExistsByHash(Study::class)],
|
||||
'content' => 'required',
|
||||
];
|
||||
}
|
||||
}
|
@ -131,7 +131,7 @@ public function getReportStatusLedAttribute(): string
|
||||
{
|
||||
$color = match ($this->report_status) {
|
||||
ReportStatus::Unread => 'bg-white',
|
||||
ReportStatus::Opened => 'bg-secondary',
|
||||
// ReportStatus::Opened => 'bg-secondary',
|
||||
ReportStatus::Preliminary => 'bg-info',
|
||||
ReportStatus::Finalized => 'bg-primary',
|
||||
ReportStatus::Approved => 'bg-success',
|
||||
|
367
resources/views/staff/reports/create.blade.php
Normal file
367
resources/views/staff/reports/create.blade.php
Normal file
@ -0,0 +1,367 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>CKEditor 5 Sample</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.ckeditor.com/ckeditor5/44.1.0/ckeditor5.css" crossorigin>
|
||||
<link rel="stylesheet"
|
||||
href="https://cdn.ckeditor.com/ckeditor5-premium-features/44.1.0/ckeditor5-premium-features.css" crossorigin>
|
||||
<link rel="stylesheet" href="./ck.css">
|
||||
<link rel="stylesheet" href="./splitter.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="mt-8">
|
||||
<div class="row ">
|
||||
<div class="container-split">
|
||||
|
||||
<div class="container__left">
|
||||
<iframe class="fixed-container" src="{{ $study->getOhifLink() }}">
|
||||
</iframe>
|
||||
</div>
|
||||
<div class="resizer"></div>
|
||||
<div class="container__right">
|
||||
<form action="{{ route('staff.report.save') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" value="{{ $study->hash }}" name="hashid">
|
||||
<div
|
||||
class="ck-editor editor-container editor-container_classic-editor editor-container_include-word-count fixed-container"
|
||||
id="editor-container">
|
||||
<div class="editor-container__editor">
|
||||
<textarea id="editor" name="content">
|
||||
{!! $report?->getContent() !!}
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="editor_container__word-count" id="editor-word-count"></div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/44.1.0/ckeditor5.umd.js" crossorigin></script>
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5-premium-features/44.1.0/ckeditor5-premium-features.umd.js"
|
||||
crossorigin></script>
|
||||
<script src="./splitter.js"></script>
|
||||
<script>
|
||||
const {
|
||||
ClassicEditor,
|
||||
Autoformat,
|
||||
Autosave,
|
||||
BlockQuote,
|
||||
Bold,
|
||||
CloudServices,
|
||||
Essentials,
|
||||
FindAndReplace,
|
||||
FontBackgroundColor,
|
||||
FontColor,
|
||||
FontFamily,
|
||||
FontSize,
|
||||
Heading,
|
||||
Highlight,
|
||||
Indent,
|
||||
IndentBlock,
|
||||
Italic,
|
||||
List,
|
||||
ListProperties,
|
||||
PageBreak,
|
||||
Paragraph,
|
||||
PasteFromOffice,
|
||||
RemoveFormat,
|
||||
SourceEditing,
|
||||
Table,
|
||||
TableCaption,
|
||||
TableCellProperties,
|
||||
TableColumnResize,
|
||||
TableProperties,
|
||||
TableToolbar,
|
||||
TextTransformation,
|
||||
Underline,
|
||||
WordCount
|
||||
} = window.CKEDITOR;
|
||||
//const { ExportPdf, ExportWord, ImportWord, MultiLevelList, PasteFromOfficeEnhanced, Template } = window.CKEDITOR_PREMIUM_FEATURES;
|
||||
|
||||
const LICENSE_KEY =
|
||||
//'GPL';
|
||||
'eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE3MzY4MTI3OTksImp0aSI6IjRkNDQyODQ2LWIwNDUtNDU1Ny1hZTY0LTM3ZDE0OTdkYWIxNSIsInVzYWdlRW5kcG9pbnQiOiJodHRwczovL3Byb3h5LWV2ZW50LmNrZWRpdG9yLmNvbSIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsiY2xvdWQiLCJkcnVwYWwiLCJzaCJdLCJ3aGl0ZUxhYmVsIjp0cnVlLCJsaWNlbnNlVHlwZSI6InRyaWFsIiwiZmVhdHVyZXMiOlsiKiJdLCJ2YyI6ImIwYmRlYmVjIn0.fhO6LXEF3lP_QpHQY2c9hPnensaY2_0AbfgJ0p4wUlQFmIyn-9dwe5rbMh9SdZYgoqOY5J4-zN0-JfO2vFm3vg';
|
||||
|
||||
const editorConfig = {
|
||||
toolbar: {
|
||||
items: [
|
||||
'sourceEditing',
|
||||
'importWord',
|
||||
'exportWord',
|
||||
'exportPdf',
|
||||
'findAndReplace',
|
||||
'|',
|
||||
'heading',
|
||||
'|',
|
||||
'fontSize',
|
||||
'fontFamily',
|
||||
'fontColor',
|
||||
'fontBackgroundColor',
|
||||
'|',
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'removeFormat',
|
||||
'|',
|
||||
'pageBreak',
|
||||
'link',
|
||||
'insertTable',
|
||||
'insertTemplate',
|
||||
'highlight',
|
||||
'blockQuote',
|
||||
'|',
|
||||
'bulletedList',
|
||||
'numberedList',
|
||||
'multiLevelList',
|
||||
'outdent',
|
||||
'indent'
|
||||
],
|
||||
shouldNotGroupWhenFull: false
|
||||
},
|
||||
plugins: [
|
||||
Autoformat,
|
||||
Autosave,
|
||||
BlockQuote,
|
||||
Bold,
|
||||
CloudServices,
|
||||
Essentials,
|
||||
FindAndReplace,
|
||||
FontBackgroundColor,
|
||||
FontColor,
|
||||
FontFamily,
|
||||
FontSize,
|
||||
Heading,
|
||||
Highlight,
|
||||
Indent,
|
||||
IndentBlock,
|
||||
Italic,
|
||||
List,
|
||||
ListProperties,
|
||||
PageBreak,
|
||||
Paragraph,
|
||||
PasteFromOffice,
|
||||
RemoveFormat,
|
||||
SourceEditing,
|
||||
Table,
|
||||
TableCaption,
|
||||
TableCellProperties,
|
||||
TableColumnResize,
|
||||
TableProperties,
|
||||
TableToolbar,
|
||||
TextTransformation,
|
||||
Underline,
|
||||
/*
|
||||
ExportPdf,
|
||||
ExportWord,
|
||||
ImportWord,
|
||||
Template,
|
||||
MultiLevelList,
|
||||
PasteFromOfficeEnhanced,
|
||||
*/
|
||||
WordCount
|
||||
],
|
||||
exportPdf: {
|
||||
stylesheets: [
|
||||
/* This path should point to application stylesheets. */
|
||||
/* See: https://ckeditor.com/docs/ckeditor5/latest/features/converters/export-pdf.html */
|
||||
'./ck.css',
|
||||
/* Export PDF needs access to stylesheets that style the content. */
|
||||
'https://cdn.ckeditor.com/ckeditor5/44.1.0/ckeditor5.css',
|
||||
'https://cdn.ckeditor.com/ckeditor5-premium-features/44.1.0/ckeditor5-premium-features.css'
|
||||
],
|
||||
fileName: 'export-pdf-demo.pdf',
|
||||
converterOptions: {
|
||||
format: 'Tabloid',
|
||||
margin_top: '20mm',
|
||||
margin_bottom: '20mm',
|
||||
margin_right: '24mm',
|
||||
margin_left: '24mm',
|
||||
page_orientation: 'portrait'
|
||||
}
|
||||
},
|
||||
exportWord: {
|
||||
stylesheets: [
|
||||
/* This path should point to application stylesheets. */
|
||||
/* See: https://ckeditor.com/docs/ckeditor5/latest/features/converters/export-word.html */
|
||||
'./ck.css',
|
||||
/* Export Word needs access to stylesheets that style the content. */
|
||||
'https://cdn.ckeditor.com/ckeditor5/44.1.0/ckeditor5.css',
|
||||
'https://cdn.ckeditor.com/ckeditor5-premium-features/44.1.0/ckeditor5-premium-features.css'
|
||||
],
|
||||
fileName: 'export-word-demo.docx',
|
||||
converterOptions: {
|
||||
document: {
|
||||
orientation: 'portrait',
|
||||
size: 'Tabloid',
|
||||
margins: {
|
||||
top: '20mm',
|
||||
bottom: '20mm',
|
||||
right: '24mm',
|
||||
left: '24mm'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
supportAllValues: true
|
||||
},
|
||||
fontSize: {
|
||||
options: [10, 12, 14, 'default', 18, 20, 22],
|
||||
supportAllValues: true
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{
|
||||
model: 'paragraph',
|
||||
title: 'Paragraph',
|
||||
class: 'ck-heading_paragraph'
|
||||
},
|
||||
{
|
||||
model: 'heading1',
|
||||
view: 'h1',
|
||||
title: 'Heading 1',
|
||||
class: 'ck-heading_heading1'
|
||||
},
|
||||
{
|
||||
model: 'heading2',
|
||||
view: 'h2',
|
||||
title: 'Heading 2',
|
||||
class: 'ck-heading_heading2'
|
||||
},
|
||||
{
|
||||
model: 'heading3',
|
||||
view: 'h3',
|
||||
title: 'Heading 3',
|
||||
class: 'ck-heading_heading3'
|
||||
},
|
||||
{
|
||||
model: 'heading4',
|
||||
view: 'h4',
|
||||
title: 'Heading 4',
|
||||
class: 'ck-heading_heading4'
|
||||
},
|
||||
{
|
||||
model: 'heading5',
|
||||
view: 'h5',
|
||||
title: 'Heading 5',
|
||||
class: 'ck-heading_heading5'
|
||||
},
|
||||
{
|
||||
model: 'heading6',
|
||||
view: 'h6',
|
||||
title: 'Heading 6',
|
||||
class: 'ck-heading_heading6'
|
||||
}
|
||||
]
|
||||
},
|
||||
initialData:
|
||||
'',
|
||||
licenseKey: LICENSE_KEY,
|
||||
link: {
|
||||
addTargetToExternalLinks: true,
|
||||
defaultProtocol: 'https://',
|
||||
decorators: {
|
||||
toggleDownloadable: {
|
||||
mode: 'manual',
|
||||
label: 'Downloadable',
|
||||
attributes: {
|
||||
download: 'file'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
list: {
|
||||
properties: {
|
||||
styles: true,
|
||||
startIndex: true,
|
||||
reversed: true
|
||||
}
|
||||
},
|
||||
menuBar: {
|
||||
isVisible: true
|
||||
},
|
||||
placeholder: 'Type or paste your content here!',
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties']
|
||||
},
|
||||
template: {
|
||||
definitions: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
description: 'Simple introduction to an article',
|
||||
icon: '<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">\n <g id="icons/article-image-right">\n <rect id="icon-bg" width="45" height="45" rx="2" fill="#A5E7EB"/>\n <g id="page" filter="url(#filter0_d_1_507)">\n <path d="M9 41H36V12L28 5H9V41Z" fill="white"/>\n <path d="M35.25 12.3403V40.25H9.75V5.75H27.7182L35.25 12.3403Z" stroke="#333333" stroke-width="1.5"/>\n </g>\n <g id="image">\n <path id="Rectangle 22" d="M21.5 23C21.5 22.1716 22.1716 21.5 23 21.5H31C31.8284 21.5 32.5 22.1716 32.5 23V29C32.5 29.8284 31.8284 30.5 31 30.5H23C22.1716 30.5 21.5 29.8284 21.5 29V23Z" fill="#B6E3FC" stroke="#333333"/>\n <path id="Vector 1" d="M24.1184 27.8255C23.9404 27.7499 23.7347 27.7838 23.5904 27.9125L21.6673 29.6268C21.5124 29.7648 21.4589 29.9842 21.5328 30.178C21.6066 30.3719 21.7925 30.5 22 30.5H32C32.2761 30.5 32.5 30.2761 32.5 30V27.7143C32.5 27.5717 32.4391 27.4359 32.3327 27.3411L30.4096 25.6268C30.2125 25.451 29.9127 25.4589 29.7251 25.6448L26.5019 28.8372L24.1184 27.8255Z" fill="#44D500" stroke="#333333" stroke-linejoin="round"/>\n <circle id="Ellipse 1" cx="26" cy="25" r="1.5" fill="#FFD12D" stroke="#333333"/>\n </g>\n <rect id="Rectangle 23" x="13" y="13" width="12" height="2" rx="1" fill="#B4B4B4"/>\n <rect id="Rectangle 24" x="13" y="17" width="19" height="2" rx="1" fill="#B4B4B4"/>\n <rect id="Rectangle 25" x="13" y="21" width="6" height="2" rx="1" fill="#B4B4B4"/>\n <rect id="Rectangle 26" x="13" y="25" width="6" height="2" rx="1" fill="#B4B4B4"/>\n <rect id="Rectangle 27" x="13" y="29" width="6" height="2" rx="1" fill="#B4B4B4"/>\n <rect id="Rectangle 28" x="13" y="33" width="16" height="2" rx="1" fill="#B4B4B4"/>\n </g>\n <defs>\n <filter id="filter0_d_1_507" x="9" y="5" width="28" height="37" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">\n <feFlood flood-opacity="0" result="BackgroundImageFix"/>\n <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>\n <feOffset dx="1" dy="1"/>\n <feComposite in2="hardAlpha" operator="out"/>\n <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.29 0"/>\n <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_507"/>\n <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_507" result="shape"/>\n </filter>\n </defs>\n</svg>\n',
|
||||
data: "<h2>Introduction</h2><p>In today's fast-paced world, keeping up with the latest trends and insights is essential for both personal growth and professional development. This article aims to shed light on a topic that resonates with many, providing valuable information and actionable advice. Whether you're seeking to enhance your knowledge, improve your skills, or simply stay informed, our comprehensive analysis offers a deep dive into the subject matter, designed to empower and inspire our readers.</p>"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
configUpdateAlert(editorConfig);
|
||||
|
||||
ClassicEditor.create(document.querySelector('#editor'), editorConfig).then(editor => {
|
||||
const wordCount = editor.plugins.get('WordCount');
|
||||
document.querySelector('#editor-word-count').appendChild(wordCount.wordCountContainer);
|
||||
|
||||
document.querySelector('#editor-menu-bar').appendChild(editor.ui.view.menuBarView.element);
|
||||
|
||||
return editor;
|
||||
});
|
||||
|
||||
/**
|
||||
* This function exists to remind you to update the config needed for premium features.
|
||||
* The function can be safely removed. Make sure to also remove call to this function when doing so.
|
||||
*/
|
||||
function configUpdateAlert(config) {
|
||||
if (configUpdateAlert.configUpdateAlertShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isModifiedByUser = (currentValue, forbiddenValue) => {
|
||||
if (currentValue === forbiddenValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const valuesToUpdate = [];
|
||||
|
||||
configUpdateAlert.configUpdateAlertShown = true;
|
||||
|
||||
if (!isModifiedByUser(config.licenseKey, '<YOUR_LICENSE_KEY>')) {
|
||||
valuesToUpdate.push('LICENSE_KEY');
|
||||
}
|
||||
|
||||
if (valuesToUpdate.length) {
|
||||
window.alert(
|
||||
[
|
||||
'Please update the following values in your editor config',
|
||||
'to receive full access to Premium Features:',
|
||||
'',
|
||||
...valuesToUpdate.map(value => ` - ${value}`)
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -1,10 +1,15 @@
|
||||
<table>
|
||||
<table class="table table-sm">
|
||||
@foreach ($reports as $report)
|
||||
<tr>
|
||||
<td>{{ $report->created_at }}</td>
|
||||
<td>{{ $report->report_status->name }}</td>
|
||||
<td>{{ $report->radiologist?->name }}</td>
|
||||
<td><a class="btn btn-primary btn-xs" target="_blank" href="{{ $report->viewUrl() }}"></a></td>
|
||||
<td><a class="btn btn-info btn-xs" target="_blank" href="{{ $report->downloadUrl() }}"></a></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
<a class="btn btn-secondary btn-xs" href="{{ route('staff.report.create', $study->hash) }}">
|
||||
Create
|
||||
</a>
|
||||
</table>
|
||||
|
@ -87,6 +87,8 @@
|
||||
Route::get('popup', [ReportController::class, 'popup'])->name('popup');
|
||||
Route::get('view/{uuid}', [ReportController::class, 'view'])->name('view');
|
||||
Route::get('download/{uuid}', [ReportController::class, 'download'])->name('download');
|
||||
Route::get('create/{hashid}', [ReportController::class, 'create'])->name('create');
|
||||
Route::post('save', [ReportController::class, 'save'])->name('save');
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user