feat: Implement Document Module with Dropzone file upload functionality
- Added DocumentController for handling document uploads and management. - Created Document model with necessary attributes and relationships. - Implemented DocumentService for business logic related to documents. - Set up routes for document management in both web and API contexts. - Developed views for document upload using Dropzone for file handling. - Included necessary assets and styles for the Document module. - Created migration for documents table with appropriate fields. - Added configuration and service provider for the Document module.
This commit is contained in:
0
Modules/Document/resources/assets/js/app.js
Normal file
0
Modules/Document/resources/assets/js/app.js
Normal file
0
Modules/Document/resources/assets/sass/app.scss
Normal file
0
Modules/Document/resources/assets/sass/app.scss
Normal file
0
Modules/Document/resources/views/.gitkeep
Normal file
0
Modules/Document/resources/views/.gitkeep
Normal file
@@ -0,0 +1,45 @@
|
||||
@props(['dropzoneId', 'uploadUrl', 'inputName', 'message' => 'Drop files here or click to upload.', 'formId'])
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<div class="needsclick dropzone" id="{{ $dropzoneId }}">
|
||||
<div class="dz-message">
|
||||
<div class="mb-3">
|
||||
<i class="display-5 text-muted ri-upload-cloud-2-fill"></i>
|
||||
</div>
|
||||
|
||||
<p class="fs-14">{{ $message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('js')
|
||||
<script>
|
||||
Dropzone.autoDiscover = false;
|
||||
window.uploadedDocumentMap = window.uploadedDocumentMap || {};
|
||||
|
||||
$(function() {
|
||||
let myDropzone = new Dropzone("#{{ $dropzoneId }}", {
|
||||
url: '{{ $uploadUrl }}',
|
||||
maxFilesize: 5,
|
||||
acceptedFiles: '.pdf,.jpeg,.jpg,.png,.gif',
|
||||
addRemoveLinks: true,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': "{{ csrf_token() }}"
|
||||
},
|
||||
success: function(file, response) {
|
||||
$('#{{ $formId }}').append(
|
||||
'<input type="hidden" name="{{ $inputName }}[]" value="' + response
|
||||
.name + '">');
|
||||
uploadedDocumentMap[file.name] = response.name;
|
||||
},
|
||||
removedfile: function(file) {
|
||||
file.previewElement.remove();
|
||||
var name = uploadedDocumentMap[file.name] || file.file_name;
|
||||
$('#{{ $formId }}').find('input[name="{{ $inputName }}[]"][value="' +
|
||||
name + '"]').remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
88
Modules/Document/resources/views/document/form.blade.php
Normal file
88
Modules/Document/resources/views/document/form.blade.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
|
||||
<form method="POST" action="{{ route('documents.dropzone.upload') }}" class="dropzone" id="mainForm"
|
||||
enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
{{ html()->label('Title')->for('title') }}
|
||||
{{ html()->span('*')->class('text-danger') }}
|
||||
{{ html()->text('title')->id('docTitle')->class('form-control')->placeholder('Enter Title')->required() }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ html()->label('Select Model')->class('form-label')->for('model') }}
|
||||
{{ html()->span('*')->class('text-danger') }}
|
||||
{{ html()->select('model')->id('modelSelect')->class('form-select')->required()->options(['' => '-- Select --'] + $modelOptions->toArray()) }}
|
||||
</div>
|
||||
|
||||
<div class="dropzone-previews mb-3"></div>
|
||||
|
||||
<div class="dz-message mb-3">
|
||||
<p class="fs-14">Drop files here or click to upload.</p>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary" id="submitAll">Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@push('js')
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
const myDropzone = new Dropzone("#mainForm", {
|
||||
url: "{{ route('documents.dropzone.upload') }}",
|
||||
autoProcessQueue: false,
|
||||
uploadMultiple: true,
|
||||
parallelUploads: 5,
|
||||
maxFilesize: 5,
|
||||
addRemoveLinks: true,
|
||||
acceptedFiles: ".pdf,.doc,.docx,.jpg,.png",
|
||||
paramName: "file[]",
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('input[name="_token"]').value
|
||||
},
|
||||
init: function() {
|
||||
const dz = this;
|
||||
|
||||
document.getElementById("submitAll").addEventListener("click", function(e) {
|
||||
const title = document.getElementById('docTitle').value;
|
||||
const model = document.getElementById('modelSelect').value;
|
||||
|
||||
if (!title || !model) {
|
||||
alert("Please fill in both Title and Model.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dz.getQueuedFiles().length > 0) {
|
||||
dz.processQueue();
|
||||
} else {
|
||||
alert("Please select at least one file.");
|
||||
}
|
||||
});
|
||||
|
||||
dz.on("sending", function(file, xhr, formData) {
|
||||
formData.append("title", document.getElementById('docTitle').value);
|
||||
formData.append("model", document.getElementById('modelSelect').value);
|
||||
});
|
||||
|
||||
dz.on("successmultiple", function(files, response) {
|
||||
alert("Files uploaded successfully.");
|
||||
dz.removeAllFiles();
|
||||
document.getElementById('mainForm').reset();
|
||||
});
|
||||
|
||||
dz.on("errormultiple", function(files, response) {
|
||||
alert("An error occurred during upload.");
|
||||
console.error(response);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
50
Modules/Document/resources/views/document/index.blade.php
Normal file
50
Modules/Document/resources/views/document/index.blade.php
Normal file
@@ -0,0 +1,50 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<x-dashboard.breadcumb :title="$title" />
|
||||
@if ($errors->any())
|
||||
<x-flash-message type="danger" :messages="$errors->all()" />
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6 col-xl-6">
|
||||
<div class="card profile-card">
|
||||
@include('document::document.form')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- <div class="col-lg-xl-8 col-lg-9">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@php
|
||||
$columns = [
|
||||
[
|
||||
'title' => '<input type="checkbox" id="select-all">',
|
||||
'data' => 'checkbox',
|
||||
'name' => 'checkbox',
|
||||
'orderable' => false,
|
||||
'searchable' => false,
|
||||
'printable' => false,
|
||||
'exportable' => false,
|
||||
],
|
||||
['title' => 'Document', 'data' => 'name', 'name' => 'name'],
|
||||
['title' => 'Type', 'data' => 'type', 'name' => 'type'],
|
||||
['title' => 'Size', 'data' => 'size', 'name' => 'size'],
|
||||
['title' => 'Upload Date', 'data' => 'created_at', 'name' => 'created_at'],
|
||||
[
|
||||
'title' => 'Action',
|
||||
'data' => 'action',
|
||||
'orderable' => false,
|
||||
'searchable' => false,
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
|
||||
<x-data-table-script :route="route('gallery.index')" :reorder="route('gallery.reorder')" :columns="$columns" />
|
||||
</div>
|
||||
</div>
|
||||
</div> --}}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
7
Modules/Document/resources/views/index.blade.php
Normal file
7
Modules/Document/resources/views/index.blade.php
Normal file
@@ -0,0 +1,7 @@
|
||||
@extends('document::layouts.master')
|
||||
|
||||
@section('content')
|
||||
<h1>Hello World</h1>
|
||||
|
||||
<p>Module: {!! config('document.name') !!}</p>
|
||||
@endsection
|
29
Modules/Document/resources/views/layouts/master.blade.php
Normal file
29
Modules/Document/resources/views/layouts/master.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<title>Document Module - {{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<meta name="description" content="{{ $description ?? '' }}">
|
||||
<meta name="keywords" content="{{ $keywords ?? '' }}">
|
||||
<meta name="author" content="{{ $author ?? '' }}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
|
||||
{{-- Vite CSS --}}
|
||||
{{-- {{ module_vite('build-document', 'resources/assets/sass/app.scss', storage_path('vite.hot')) }} --}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@yield('content')
|
||||
|
||||
{{-- Vite JS --}}
|
||||
{{-- {{ module_vite('build-document', 'resources/assets/js/app.js', storage_path('vite.hot')) }} --}}
|
||||
</body>
|
Reference in New Issue
Block a user