wip - attachments

This commit is contained in:
Masroor Ehsan 2025-01-06 17:56:09 +06:00
parent e6376c7d92
commit b7efd18571
5 changed files with 203 additions and 74 deletions

View File

@ -15,6 +15,7 @@
use Illuminate\Support\Str;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class Study extends BaseModel implements HasMedia
{
@ -22,6 +23,7 @@ class Study extends BaseModel implements HasMedia
use InteractsWithMedia;
public const string MEDIA_COLLECTION = 'attachments';
public const string FALLBACK_IMAGE = 'imgs/pdf.png';
public function details(): HasOne
{
@ -265,6 +267,26 @@ public function getPriorityIcon(): string
};
}
public function registerMediaConversions(?Media $media = null): void
{
// $media->extension
$this->addMediaConversion('tn')
->width(48)
->height(48)
->sharpen(10)
->performOnCollections(Study::MEDIA_COLLECTION)
->nonQueued();
}
public function registerMediaCollections(): void
{
$this->addMediaCollection(self::MEDIA_COLLECTION)
->useFallbackUrl(asset(self::FALLBACK_IMAGE))
->useFallbackUrl(asset(self::FALLBACK_IMAGE), 'tn')
->useFallbackPath(public_path(self::FALLBACK_IMAGE))
->useFallbackPath(public_path(self::FALLBACK_IMAGE), 'tn');
}
protected function casts(): array
{
return [

View File

@ -2,6 +2,7 @@
use App\Models\User;
use App\Services\AuditTrail\ActivityLogger;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
if (! function_exists('_h')) {
function _h(int $key): string
@ -72,10 +73,11 @@ function may(BackedEnum|iterable|string $perm): bool
return auth()->user()->can($perm);
}
}
if (! function_exists('human_filesize')) {
function human_filesize(int $bytes, $dec = 0): string
{
$size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$size = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$factor = floor((strlen($bytes) - 1) / 3);
if ($factor == 0) {
$dec = 0;
@ -84,3 +86,28 @@ function human_filesize(int $bytes, $dec = 0): string
return sprintf("%.{$dec}f %s", $bytes / (1024 ** $factor), $size[$factor]);
}
}
if (! function_exists('chomp')) {
function chomp(string $s, int $len, int $right = 6): string
{
$length = strlen($s);
if ($length <= ($len + $right)) {
return $s;
}
$start = substr($s, 0, $len);
$end = substr($s, -$right);
return $start . '...' . $end;
}
}
if (! function_exists('thumb_url')) {
function thumb_url(Media $media): string
{
if ($media->mime_type === 'application/pdf') {
return asset('imgs/pdf.png');
}
return $media->getUrl('tn') ?? asset('imgs/pdf.png');
}
}

BIN
resources/imgs/pdf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -12,12 +12,13 @@
@extends('layouts.layoutMaster')
@section('title', 'Worklist')
@section('title', 'Info')
@section('vendor-style')
@vite([
'resources/fontawesome/scss/fontawesome.scss',
'resources/fontawesome/scss/light.scss',
'resources/assets/vendor/libs/dropzone/dropzone.scss'
])
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/ui/trumbowyg.min.css"
integrity="sha512-Fm8kRNVGCBZn0sPmwJbVXlqfJmPC13zRsMElZenX6v721g/H7OukJd8XzDEBRQ2FSATK8xNF9UYvzsCtUpfeJg=="
@ -33,111 +34,135 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/plugins/cleanpaste/trumbowyg.cleanpaste.min.js"
integrity="sha512-UInqT8f+K1tkck6llPo0HDxlT/Zxv8t4OGeCuVfsIlXLrnP1ZKDGb+tBsBPMqDW15OcmV8NDfQe9+EaAG4aXeg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
@endsection
@section('page-script')
<script>
$('textarea').trumbowyg({
$('.tw_ed').trumbowyg({
btns: [
['strong', 'em'],
['removeformat'],
['viewHTML'],
['fullscreen']
],
removeformatPasted: true,
autogrow: true,
resetCss: true
removeformatPasted: true
});
Dropzone.options.fileDropzone = {
paramName: 'file',
maxFilesize: 5, // MB
acceptedFiles: '.pdf,.jpg,.jpeg,.png',
uploadMultiple: true,
parallelUploads: 3,
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
success: function (file, response) {
console.log(response.success);
(function () {
// previewTemplate: Updated Dropzone default previewTemplate
// ! Don't change it unless you really know what you are doing
const previewTemplate = `<div class="dz-preview dz-file-preview">
<div class="dz-details">
<div class="dz-thumbnail">
<img data-dz-thumbnail>
<span class="dz-nopreview">No preview</span>
<div class="dz-success-mark"></div>
<div class="dz-error-mark"></div>
<div class="dz-error-message"><span data-dz-errormessage></span></div>
<div class="progress">
<div class="progress-bar progress-bar-primary" role="progressbar" aria-valuemin="0" aria-valuemax="100" data-dz-uploadprogress></div>
</div>
</div>
<div class="dz-filename" data-dz-name></div>
<div class="dz-size" data-dz-size></div>
</div>
</div>`;
const dropzoneMulti = document.querySelector('#dropzone-multi');
if (dropzoneMulti) {
const myDropzoneMulti = new Dropzone(dropzoneMulti, {
paramName: 'file',
maxFilesize: 5, // MB
acceptedFiles: '.pdf,.jpg,.jpeg,.png',
uploadMultiple: true,
parallelUploads: 2,
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
},
previewTemplate: previewTemplate,
addRemoveLinks: true
});
}
};
})();
</script>
@endsection
@section('content')
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('History') }}
</h2>
<h4>Clinical Information</h4>
<div class="row g-6">
<div class="col-8">
<form action="{{ route('staff.history.save', $details->hash) }}" method="post">
@csrf
<input type="hidden" name="study_id" value="{{ $details->hash }}">
<h5>Clinical History</h5>
<div class="p-4 border-gray-100">
<textarea name="clinical_history" id="clinical_history" cols="90"
rows="10">{!! $details->clinical_history !!}</textarea>
</div>
<x-section-border/>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('History') }}
</h2>
<h5>surgical history</h5>
<div class="p-4 border-gray-100">
<textarea name="surgical_history" id="surgical_history" cols="90"
rows="10">{!! $details->surgical_history !!}</textarea>
</div>
<x-section-border/>
<h4>Clinical Information</h4>
<h5>lab results</h5>
<div class="p-4 border-gray-100">
<textarea name="lab_results" id="lab_results" cols="90"
rows="10">{!! $details->lab_results !!}</textarea>
</div>
<x-section-border/>
<form action="{{ route('staff.history.save', $details->hash) }}" method="post">
@csrf
<input type="hidden" name="study_id" value="{{ $details->hash }}">
<h5>Clinical History</h5>
<div class="p-4 border-gray-100">
<div class="tw_ed" name="clinical_history" id="clinical_history"
>{!! $details->clinical_history !!}</div class="tw_ed">
</div>
<x-section-border/>
<h5>clinical diagnosis</h5>
<div class="p-4 border-gray-100">
<textarea name="clinical_diagnosis" id="clinical_diagnosis" cols="90"
rows="10">{!! $details->clinical_diagnosis !!}</textarea>
<h5>surgical history</h5>
<div class="p-4 border-gray-100">
<div class="tw_ed" name="surgical_history" id="surgical_history"
>{!! $details->surgical_history !!}</div class="tw_ed">
</div>
<x-section-border/>
<h5>lab results</h5>
<div class="p-4 border-gray-100">
<div class="tw_ed" name="lab_results" id="lab_results"
>{!! $details->lab_results !!}</div class="tw_ed">
</div>
<x-section-border/>
<h5>clinical diagnosis</h5>
<div class="p-4 border-gray-100">
<div class="tw_ed" name="clinical_diagnosis" id="clinical_diagnosis"
>{!! $details->clinical_diagnosis !!}</div class="tw_ed">
</div>
<button type="submit">Save</button>
</form>
</div>
<button type="submit">Save</button>
</form>
<hr>
<h1>Upload Files</h1>
<div class="col-4">
<h4>Attachments</h4>
<!-- Dropzone area -->
<form action="{{ route('staff.attachment.upload', $study->hash) }}" class="dropzone needsclick"
id="dropzone-multi">
@csrf
<div class="dz-message needsclick">
Drop attachments here or click to upload
<span class="note needsclick">Allowed files: <span class="fw-medium">JPG, PNG, PDF</span></span>
</div>
<div class="fallback">
<input name="file" type="file"/>
</div>
</form>
<!-- List of already uploaded files -->
@include('staff.history.partials._uploaded-studies-list', ['study' => $study])
</div>
<!-- List of already uploaded files -->
<div class="uploaded-files">
<h2>Uploaded Files</h2>
<ul>
@foreach ($study->getMedia(\App\Models\Study::MEDIA_COLLECTION) as $media)
<li>
<i class="fa {{ $media->mime_type == 'application/pdf' ? 'fa-file-pdf' : 'fa-file-image' }}"></i>
<a href="{{ $media->getUrl() }}" target="_blank">{{ $media->file_name }}</a>
({{ $media->human_readable_size }}) - Uploaded on {{ $media->created_at->format('Y-m-d H:i') }}
<form action="{{ route('staff.attachment.delete', [$study->hash, $media->id]) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger btn-sm">Delete</button>
</form>
</li>
@endforeach
</ul>
</div>
<!-- Dropzone area -->
<form action="{{ route('staff.attachment.upload', $study->hash) }}" class="dropzone" id="file-dropzone">
@csrf
</form>
@endsection

View File

@ -0,0 +1,55 @@
<div class="table-responsive text-nowrap">
<table class="table dataTable no-footer table-sm">
<thead>
<tr>
<th>File</th>
<th>Size</th>
<th>Uploaded</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
@foreach ($study->getMedia(\App\Models\Study::MEDIA_COLLECTION) as $media)
<tr>
<td>
<div class="d-flex justify-content-start align-items-center">
<div class="avatar-wrapper">
<div class="avatar me-2">
<img class="rounded" src="{{ thumb_url($media) }}"/>
</div>
</div>
<div class="d-flex flex-column">
<a class="text-heading fw-medium" target="_blank" href="{{ $media->getUrl() }}">
{{ chomp($media->file_name, 20) }}
</a>
</div>
</div>
</td>
<td>
{{ $media->human_readable_size }}
</td>
<td>
{{ $media->created_at->format('d.m.Y h:iA') }}
</td>
<td>
<div class="d-flex align-items-end">
<form action="{{ route('staff.attachment.delete', [$study->hash, $media->id]) }}"
method="POST" style="display:inline;">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-google-plus btn-xs btn-icon">
<i class="fa fa-trash"></i>
</button>
</form>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>