first change

This commit is contained in:
2025-07-27 17:40:56 +05:45
commit f8b9a6725b
3152 changed files with 229528 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<?php
namespace Modules\Template\Emails;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class SendMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $data;
/**
* Create a new message instance.
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*/
// public function build(): self
// {
// return $this->view('mail.template');
// }
public function envelope(): Envelope
{
return new Envelope(
subject: $this->data['subject'],
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'mail.template'
);
}
// /**
// * Get the attachments for the message.
// *
// * @return array
// */
public function attachments(): array
{
$attachments = [];
if (isset($this->data['documentPaths'])) {
foreach ($this->data['documentPaths'] as $path) {
$attachments[] = Attachment::fromPath($path);
}
}
if (isset($this->data['mergePdf'])) {
$attachments[] = Attachment::fromPath($this->data['mergePdf']);
}
return $attachments;
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace Modules\Template\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Modules\Template\Models\Template;
use Modules\Template\Repositories\TemplateInterface;
use Yajra\DataTables\Facades\DataTables;
class TemplateController extends Controller
{
private $template;
public function __construct(
TemplateInterface $template,
) {
$this->template = $template;
}
/**
* Display a listing of the resource.
*/
public function index()
{
$data['title'] = 'Template List';
$model = Template::query()->latest();
if (request()->ajax()) {
return DataTables::eloquent($model)
->addIndexColumn()
->addColumn('action', 'template::template.partials.action')
->rawColumns(['action'])
->toJson();
}
return view('template::template.index', $data);
}
public function create()
{
$data['title'] = 'Create Template';
$data['editable'] = false;
$data['fields'] = Template::FIELDS;
$data['type'] = Template::TYPE;
return view('template::template.create', $data);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$inputData = $request->all();
try {
$this->template->create($inputData);
flash()->success('Template has been created!');
} catch (\Throwable $th) {
flash()->error($th->getMessage());
}
return redirect()->route('template.index');
}
/**
* Show the specified resource.
*/
public function show($id)
{
abort('404');
$data['title'] = 'View Template';
$data['template'] = $this->template->findById($id);
return view('template::template.show', $data);
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
$data['title'] = 'Edit Template';
$data['editable'] = true;
$data['template'] = $this->template->findById($id);
$data['fields'] = Template::FIELDS;
$data['type'] = Template::TYPE;
return view('template::template.edit', $data);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id): RedirectResponse
{
$inputData = $request->except(['_method', '_token']);
try {
$this->template->update($id, $inputData);
flash()->success('Template has been updated!');
return redirect()->route('template.index')->withSuccess('Template has been updated!');
} catch (\Throwable $th) {
return redirect()->back()->withErrors($th->getMessage());
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
try {
$this->template->delete($id);
flash()->success('Template has been deleted!');
} catch (\Throwable $th) {
flash()->error($th->getMessage());
}
return response()->json(['status' => true, 'message' => 'Template has been deleted!']);
}
public function changeStatus(Request $request)
{
try {
$taskModel = $this->template->findById($request->id);
$taskModel->status = $request->status;
$taskModel->save();
return response()->json([
'status' => true,
'msg' => 'Status Changed',
], 200);
} catch (\Throwable $th) {
return response()->json([
'status' => false,
'msg' => $th->getMessage(),
], 400);
}
}
public function findByAjax(Request $request)
{
try {
$template = $this->template->findById($request->id);
return response()->json([
'status' => true,
'data' => $template,
], 200);
} catch (\Throwable $th) {
return response()->json([
'status' => false,
'msg' => $th->getMessage(),
], 400);
}
}
public function fileUpload(Request $request)
{
if ($request->hasFile('upload')) {
$file = $request->file('upload');
$path = 'uploads/ckeditor';
$imagePath = uploadImage($file, $path);
$CKEditorFuncNum = $request->input('CKEditorFuncNum');
$url = asset('storage/' . $imagePath);
$response = "<script>window.parent.CKEDITOR.tools.callFunction($CKEditorFuncNum, '$url')</script>";
echo $response;
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Template\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Template extends Model
{
use HasFactory;
protected $guarded = [];
const FIELDS = [
'name', 'email', 'mobile',
];
const TYPE = [
'email' => 'Email',
// 'sms' => 'SMS',
// 'news_letter' => 'News Letter',
];
protected static function booted()
{
static::creating(function ($model) {
$model->alias = \Str::slug($model->title);
});
}
}

View File

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\Template\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
/**
* The event handler mappings for the application.
*
* @var array<string, array<int, string>>
*/
protected $listen = [];
/**
* Indicates if events should be discovered.
*
* @var bool
*/
protected static $shouldDiscoverEvents = true;
/**
* Configure the proper event listeners for email verification.
*
* @return void
*/
protected function configureEmailVerification(): void
{
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Modules\Template\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*/
public function boot(): void
{
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware('web')->group(module_path('Template', '/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Template', '/routes/api.php'));
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace Modules\Template\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Modules\Template\Repositories\TemplateInterface;
use Modules\Template\Repositories\TemplateRepository;
class TemplateServiceProvider extends ServiceProvider
{
protected string $moduleName = 'Template';
protected string $moduleNameLower = 'template';
/**
* Boot the application events.
*/
public function boot(): void
{
$this->registerCommands();
$this->registerCommandSchedules();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
}
/**
* Register the service provider.
*/
public function register(): void
{
$this->app->bind(TemplateInterface::class, TemplateRepository::class);
$this->app->register(EventServiceProvider::class);
$this->app->register(RouteServiceProvider::class);
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands(): void
{
// $this->commands([]);
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules(): void
{
// $this->app->booted(function () {
// $schedule = $this->app->make(Schedule::class);
// $schedule->command('inspire')->hourly();
// });
}
/**
* Register translations.
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
$this->loadJsonTranslationsFrom($langPath);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
}
}
/**
* Register config.
*/
protected function registerConfig(): void
{
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower . '.php')], 'config');
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
}
/**
* Register views.
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'resources/views');
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
$componentNamespace = str_replace('/', '\\', config('modules.namespace') . '\\' . $this->moduleName . '\\' . ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', '')));
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
}
/**
* Get the services provided by the provider.
*
* @return array<string>
*/
public function provides(): array
{
return [];
}
/**
* @return array<string>
*/
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (config('view.paths') as $path) {
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
$paths[] = $path . '/modules/' . $this->moduleNameLower;
}
}
return $paths;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Modules\Template\Repositories;
use App\Interfaces\ModelInterface;
interface TemplateInterface extends ModelInterface
{
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Modules\Template\Repositories;
use Illuminate\Http\Request;
use Modules\Template\Models\Template;
class TemplateRepository implements TemplateInterface
{
public function findAll($request, callable $query = null, bool $paginate = false, int $limit = 10)
{
return Template::get();
}
public function findById($id, callable $query = null)
{
return Template::findOrFail($id);
}
public function delete($id)
{
return Template::where('id', $id)->delete();
}
public function create(array $data)
{
return Template::create($data);
}
public function update($id, array $newDetails)
{
return Template::where('id', $id)->update($newDetails);
}
public function pluck(callable $query = null)
{
return Template::where(['status' => 11])
->select('id', 'title', 'alias', 'type')
->get()
->groupBy('type')
->mapWithKeys(function ($templates, $type) {
return [
$type => $templates->mapWithKeys(function ($template) {
return [$template->id => $template->title];
}),
];
});
}
public function where($filter)
{
return Template::where($filter);
}
}

View File

@@ -0,0 +1,30 @@
{
"name": "nwidart/template",
"description": "",
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"extra": {
"laravel": {
"providers": [],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Template\\": "app/",
"Modules\\Template\\Database\\Factories\\": "database/factories/",
"Modules\\Template\\Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Modules\\Template\\Tests\\": "tests/"
}
}
}

View File

View File

@@ -0,0 +1,5 @@
<?php
return [
'name' => 'Template',
];

View File

@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('templates', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable();
$table->string('alias')->nullable();
$table->text('subject')->nullable();
$table->longText('message')->nullable();
$table->enum('type', ['email', 'sms', 'news_letter'])->nullable(); //email,sms
$table->integer('status')->nullable()->default(11);
$table->integer('createdby')->nullable();
$table->integer('updatedby')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('templates');
}
};

View File

@@ -0,0 +1,16 @@
<?php
namespace Modules\Template\Database\Seeders;
use Illuminate\Database\Seeder;
class TemplateDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

View File

@@ -0,0 +1,11 @@
{
"name": "Template",
"alias": "template",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\Template\\Providers\\TemplateServiceProvider"
],
"files": []
}

View File

@@ -0,0 +1,15 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.5",
"sass": "^1.69.5",
"postcss": "^8.3.7",
"vite": "^4.0.0"
}
}

View File

@@ -0,0 +1,7 @@
@extends('template::layouts.master')
@section('content')
<h1>Hello World</h1>
<p>Module: {!! config('template.name') !!}</p>
@endsection

View 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>Template 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-template', 'resources/assets/sass/app.scss') }} --}}
</head>
<body>
@yield('content')
{{-- Vite JS --}}
{{-- {{ module_vite('build-template', 'resources/assets/js/app.js') }} --}}
</body>

View File

@@ -0,0 +1,17 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
{{ html()->form('POST')->route('template.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('template::template.partials.form')
{{ html()->form()->close() }}
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@@ -0,0 +1,19 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
{{ html()->modelForm($template, 'PUT')->route('template.update', $template->id)->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('template::template.partials.form')
{{ html()->closeModelForm() }}
<!--end row-->
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@@ -0,0 +1,43 @@
@extends('layouts.app')
@section('content')
<div class="container-fluid">
<x-dashboard.breadcumb :title="$title" />
<div class="mb-2 text-end">
@can('template.create')
<a href="{{ route('template.create') }}" class="btn btn-primary btn-sm waves-effect waves-light"><i
class="ri-add-fill me-1 align-bottom"></i> Add</a>
@endcan
</div>
<div class="card" id="customerList">
<div class="card-body">
<div class="table-responsive">
@php
$columns = [
[
'title' => 'S.N',
'data' => 'DT_RowIndex',
'name' => 'DT_RowIndex',
'orderable' => false,
'searchable' => false,
'width' => '5%',
],
['title' => 'Title', 'data' => 'title', 'name' => 'title'],
['title' => 'Alias', 'data' => 'alias', 'name' => 'alias'],
['title' => 'Subject', 'data' => 'subject', 'name' => 'subject'],
[
'title' => 'Action',
'data' => 'action',
'orderable' => false,
'searchable' => false,
],
];
@endphp
<x-data-table-script :route="route('template.index')" :columns="$columns" id="templates-table" />
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,170 @@
<div class="modal fade" id="attachmentModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="emailModalLabel">Send Mail</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{ html()->form('POST')->class(['needs-validation sendMailForm'])->attributes(['novalidate'])->open() }}
<div class="card card-body bg-white">
<div class="row g-3">
<div class="col-lg-12">
{{ html()->label('To:')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->email('email')->class('form-control')->placeholder("Enter Receipent's Email")->required() }}
{{ html()->div('Enter Valid Email Address')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('template', $templateOptions)->class('form-select change-template')->placeholder('Select Template')->attributes(['data-type' => 'email'])->required() }}
</div>
<div class="col-lg-12">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('subject')->class('form-control subject')->required() }}
{{ html()->div('Subject is required!')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('attachment-editor')->required() }}
</div>
<div class="col-lg-12">
{{ html()->label('Attachments')->class('form-label') }}
<div class="document-list">
</div>
</div>
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>
@push('js')
<script>
$('body').on('change', '.change-template', function() {
id = $(this).val();
that = $(this)
if (id == '') {
toastr.error('Choose Template First');
return false;
}
$.ajax({
type: "GET",
url: '{{ route('template.findByAjax') }}',
data: {
id: id,
},
success: function(res) {
result = res.data;
if (result.status) {
that.parents('.card').find('.subject').val(result.subject);
let messageFieldId = that.parents('.card').find('.message').attr('id');
let editor = CKEDITOR.instances[messageFieldId];
if (editor) {
let currentContent = editor.getData();
editor.setData(result.message);
}
}
},
});
})
$(document).on('submit', '.sendMailForm', function(event) {
event.preventDefault();
const url = $(this).attr('action');
const method = $(this).attr('method');
let form = $(this);
let formData = new FormData(form[0]);
let myModalEl = $(this).closest('.modal').attr('id');
var emailModal = bootstrap.Modal.getOrCreateInstance($('#' + myModalEl))
const button = $(this).find('button[type="submit"]');
$.ajax({
url: url,
type: method,
data: formData,
dataType: 'json',
processData: false,
contentType: false,
headers: {
'X-CSRF-TOKEN': $("meta[name='csrf-token']").attr('content'),
},
beforeSend: () => {
button.text('Sending...');
button.prop('disabled', true);
},
success: function(response) {
if (response.status == true) {
emailModal.hide()
button.text('Send');
button.prop('disabled', false);
let messageFieldId = form.find('.message').attr('id');
let editor = CKEDITOR.instances[messageFieldId];
if (editor) {
editor.setData('');
}
form[0].reset();
toastr.success(response.msg);
}
$('#student-table').DataTable().ajax.reload();
var statusModal = bootstrap.Modal.getInstance($('#bulkStatusModal'));
statusModal.hide();
},
error: function(xhr) {
if (xhr.status == 422) {
let errors = xhr.responseJSON.errors;
$('.error-message').remove();
$.each(errors, function(key, value) {
let errorMessage = $(
'<span class="error-message text-danger mt-2"></span>'
)
.text(
value[0]);
$('#' + key).after(errorMessage);
});
} else {
console.log(xhr);
}
},
complete: () => {
button.text('Save');
button.prop('disabled', false);
}
})
})
</script>
@endpush

View File

@@ -0,0 +1,63 @@
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="emailModalLabel">Send Email</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{ html()->form('POST', $route)->class(['needs-validation sendMailForm'])->attributes(['novalidate'])->open() }}
{{ html()->hidden('ids') }}
<ul class="list-inline d-flex flex-column flex-wrap gap-2">
<li class="list-inline-item">
{{-- <span class="fw-bold">To:</span> {{ $student->name }} < {{ $student->email }}>
{{ html()->hidden('email', $student->email) }} --}}
<span class="fw-bold">To:</span></span> <span class="selected-names">
< {{ @$model->name }}>
</span>
{{-- {{ html()->hidden('email', @$model->email) }} --}}
@isset($single)
{{ html()->text('email')->value(@$model->email)->class('form-control email')->placeholder('Enter Email')->required() }}
{{ html()->div('Email is required')->class('invalid-feedback') }}
@endisset
</li>
<li class="list-inline-item">
<span class="fw-bold">From:</span> {{ setting('email') }}
</li>
</ul>
<div class="card card-body bg-white">
<div class="row g-3">
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->select('template', @$template['email'])->class('form-control change-template')->placeholder('Select Template')->attributes(['data-type' => 'email']) }}
{{-- {{ html()->div('Template is required!')->class('invalid-feedback') }} --}}
</div>
<div class="col-lg-12">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('subject')->class('form-control subject')->required() }}
{{ html()->div('Subject is required!')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('email-editor') }}
</div>
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,52 @@
<div class="modal fade" id="bulkNewsLetterModal" tabindex="-1" aria-labelledby="bulkNewsLetterModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="bulkNewsLetterModalLabel">Send News Letter</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<ul class="list-inline d-flex flex-column flex-wrap gap-2">
<li class="list-inline-item">
<span class="fw-bold">To:</span> <span class="selected-names"></span>
</li>
<li class="list-inline-item">
<span class="fw-bold">From:</span> {{ setting('email') }}
</li>
</ul>
{{ html()->form('POST', $route)->class(['needs-validation sendMailForm'])->attributes(['novalidate', 'id' => 'sendMailForm'])->open() }}
{{ html()->hidden('ids') }}
<div class="card card-body bg-white">
<div class="row g-3">
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->select('template', @$template['news_letter'])->class('form-control change-template')->placeholder('Select Template')->attributes(['data-type' => 'email']) }}
{{-- {{ html()->div('Template is required!')->class('invalid-feedback') }} --}}
</div>
<div class="col-lg-12">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('subject')->class('form-control subject')->required() }}
{{ html()->div('Subject is required!')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('newsletter-editor') }}
</div>
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,73 @@
<div class="modal fade" id="pdfModal" tabindex="-1" aria-labelledby="pdfModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="pdfModalLabel">Send Merge Pdfs</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{ html()->form('POST', $route)->class(['needs-validation sendMailForm'])->attributes(['novalidate'])->open() }}
{{ html()->hidden('ids') }}
<ul class="list-inline d-flex flex-column flex-wrap gap-2">
<li class="list-inline-item">
<span class="fw-bold">To:</span></span> <span class="selected-names">
< {{ @$model->name }}>
</span>
@isset($single)
{{ html()->text('email')->value(@$model->email)->class('form-control email')->placeholder('Enter Email')->required() }}
{{ html()->div('Email is required')->class('invalid-feedback') }}
@endisset
</li>
<li class="list-inline-item">
<span class="fw-bold">From:</span> {{ setting('email') }}
</li>
</ul>
<div class="card card-body bg-white">
<div class="row g-3">
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->select('template', @$template['email'])->class('form-control change-template')->placeholder('Select Template')->attributes(['data-type' => 'email']) }}
</div>
<div class="col-lg-12">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('subject')->class('form-control subject')->required() }}
{{ html()->div('Subject is required!')->class('invalid-feedback') }}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('pdf-editor') }}
</div>
<div class="col-lg-12">
{{ html()->label('Attachments')->class('form-label') }}
{{ html()->hidden('merge_pdf', asset(@$model->merge_pdf_file)) }}
<div class="document-list1">
<div class="avatar-md mx-auto mb-3">
<div class="avatar-title bg-danger-subtle text-secondary fs-22 rounded">
<i class="ri-file-pdf-line"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,52 @@
<div class="modal fade" id="smsModal" tabindex="-1" aria-labelledby="smsModalLabel" aria-hidden="true"
style="display: none;">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header border-0">
<h5 class="modal-title" id="smsModalLabel">Send SMS</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<ul class="list-inline d-flex flex-column flex-wrap gap-2">
<li class="list-inline-item">
<span class="fw-bold">Name:</span> {{ $student->name }}
</li>
<li class="list-inline-item">
<span class="fw-bold">Email:</span> {{ $student->email }}
</li>
<li class="list-inline-item">
<span class="fw-bold">Mobile:</span> {{ $student->mobile }}
</li>
</ul>
<div class="card">
<div class="card-body">
{{ html()->form('POST')->route('student.changeAssigne')->class(['needs-validation'])->attributes(['novalidate', 'id' => 'sendSmsForm'])->open() }}
<div class="row g-3">
{{ html()->hidden('id') }}
<div class="col-lg-12">
{{ html()->label('Select Template')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->select('template', @$template['sms'])->class('form-control change-template')->placeholder('Select Template') }}
{{-- {{ html()->div('Template is required!')->class('invalid-feedback') }} --}}
</div>
<div class="col-lg-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->textarea('message')->class('form-control message ckeditor-classic')->id('sms-editor')->required() }}
</div>
</div>
<div class="hstack justify-content-end mt-2 gap-2">
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Send</button>
</div>
{{ html()->form()->close() }}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,14 @@
<ul class="list-inline hstack mb-0 gap-2">
<li class="list-inline-item edit" data-bs-toggle="tooltip" data-bs-trigger="hover" data-bs-placement="top" title="Edit">
<a href="{{ route('template.edit', $id) }}" class="text-primary d-inline-block edit-item-btn">
<i class="ri-pencil-fill fs-16"></i>
</a>
</li>
<li class="list-inline-item" data-bs-toggle="tooltip" data-bs-trigger="hover" data-bs-placement="top" title="Remove">
<a class="text-danger d-inline-block remove-item-btn" data-bs-toggle="modal"
data-link="{{ route('template.destroy', $id) }}" data-id="{{ $id }}">
<i class="ri-delete-bin-5-fill fs-16"></i>
</a>
</li>
</ul>

View File

@@ -0,0 +1,45 @@
{{ html()->form('GET')->route('template.index')->class(['filter-form'])->attributes(['id' => 'filter-form'])->open() }}
<div class="row g-2">
<div class="col-sm-11">
<div class="row g-3">
<div class="col-sm-2">
{{ html()->date('from_date')->placeholder('Choose Date')->class('form-control form-control-sm') }}
</div>
<div class="col-sm-2">
{{ html()->date('to_date')->placeholder('Choose Date')->class('form-control form-control-sm') }}
</div>
<div class="col-sm-2">
{{ html()->select('country_id', $country)->placeholder('Select Country')->class('form-select form-select-sm select21') }}
</div>
<div class="col-sm-2">
{{ html()->select('branch_id', $branch)->placeholder('Select Branch')->class('form-select form-select-sm select21') }}
</div>
<div class="col-sm-2">
{{ html()->select('templates_id', [])->placeholder('Select template')->class('form-select form-select-sm select21') }}
</div>
<div class="col-sm-2">
{{ html()->select('sources_id', [])->placeholder('Select Source')->class('form-select form-select-sm select21') }}
</div>
</div>
<!--end row-->
</div>
<!--end col-->
<div class="col-sm-auto ms-auto">
<div class="list-grid-nav hstack gap-1">
<button type="submit" class="btn btn-warning btn-sm">
Filter</button>
<a href="javascript:void(0)" class="btn btn-danger btn-sm reset-filter">
Reset</a>
</div>
</div>
<!--end col-->
</div>
<!--end row-->
{{ html()->form()->close() }}

View File

@@ -0,0 +1,72 @@
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<div class="row gy-3">
<div class="col-md-12">
{{ html()->label('Title')->class('form-label') }}
{{ html()->span('*')->class('text-danger') }}
{{ html()->text('title')->class('form-control')->required() }}
{{ html()->div('Please enter title')->class('invalid-feedback') }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="row gy-3">
<div class="col-md-6">
{{ html()->label('Subject')->class('form-label') }}
{{ html()->text('subject')->class('form-control')->required() }}
</div>
<div class="col-md-6">
{{ html()->label('Type')->class('form-label') }}
{{ html()->select('type', $type, request()->get('type') ?? null)->class('form-select')->required() }}
</div>
<div class="col-md-12">
{{ html()->label('Message')->class('form-label') }}
{{ html()->textarea('message')->class('form-control ckeditor-classic') }}
</div>
</div>
</div>
</div>
<div class="mb-3 text-end">
<a href="{{ route('template.index') }}" class="btn btn-danger w-sm">Cancel</a>
<button type="submit" class="btn btn-success w-sm">Save</button>
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Form Fields</h5>
</div>
<div class="card-body">
<ul class="list-group">
@foreach ($fields as $list)
<li class="list-group-item"><i class="ri-file-copy-line me-2 align-middle"
onclick="copyContent('{{ $list }}')"></i>
{{ $list }}</li>
@endforeach
</ul>
</div>
</div>
</div>
</div>
@push('js')
<script>
baseUrl = "{{ url('/') }}";
const copyContent = async (list) => {
try {
await navigator.clipboard.writeText("#" + list + "");
console.log('Content copied to clipboard');
} catch (err) {
console.error('Failed to copy: ', err);
}
}
</script>
@endpush

View File

@@ -0,0 +1,7 @@
@extends('layouts.app')
@section('content')
@endsection

View File

View File

@@ -0,0 +1,19 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Template\Http\Controllers\TemplateController;
/*
*--------------------------------------------------------------------------
* API Routes
*--------------------------------------------------------------------------
*
* Here is where you can register API routes for your application. These
* routes are loaded by the RouteServiceProvider within a group which
* is assigned the "api" middleware group. Enjoy building your API!
*
*/
Route::middleware(['auth:sanctum'])->prefix('v1')->group(function () {
Route::apiResource('template', TemplateController::class)->names('template');
});

View File

@@ -0,0 +1,24 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Template\Http\Controllers\TemplateController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['middleware' => ['web', 'auth', 'permission'], 'prefix' => 'admin/'], function () {
Route::get('email-template', [TemplateController::class, 'email'])->name('template.email');
Route::get('template/findByAjax', [TemplateController::class, 'findByAjax'])->name('template.findByAjax');
Route::post('files/upload', [TemplateController::class, 'fileUpload'])->name('file.upload');
Route::resource('template', TemplateController::class)->names('template');
});

View File

View File

View File

@@ -0,0 +1,26 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
build: {
outDir: '../../public/build-template',
emptyOutDir: true,
manifest: true,
},
plugins: [
laravel({
publicDirectory: '../../public',
buildDirectory: 'build-template',
input: [
__dirname + '/resources/assets/sass/app.scss',
__dirname + '/resources/assets/js/app.js'
],
refresh: true,
}),
],
});
//export const paths = [
// 'Modules/Template/resources/assets/sass/app.scss',
// 'Modules/Template/resources/assets/js/app.js',
//];