update marketing
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 5m14s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-05-14 11:55:22 +07:00
parent f80fcc4c1b
commit 05fd3230b8
448 changed files with 17545 additions and 5128 deletions

View File

@@ -0,0 +1,62 @@
<?php
namespace Modules\MarketingAddon\App\Emails;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
use Illuminate\Mail\Mailables\Envelope;
class DynamicMail extends Mailable
{
use Queueable, SerializesModels;
private $data;
private $template;
public function __construct($template, $data)
{
$this->data = $data;
$this->template = $template;
}
public function envelope(): Envelope
{
return new Envelope(
from: new Address(
Config::get('mail.from.address'),
Config::get('mail.from.name')
),
subject: $this->template->subject,
);
}
public function content(): Content
{
return new Content(
view: 'mail.dynamic-mail',
with: [
'content' => $this->buildHtml(),
],
);
}
public function attachments(): array
{
return [];
}
protected function buildHtml()
{
$message = $this->template->message;
foreach ($this->data as $key => $value) {
$message = str_replace('[' . $key . ']', $value, $message);
}
return $message;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Modules\MarketingAddon\App\Exports;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;
use Modules\MarketingAddon\App\Models\Smsgateway;
class SmsGatewayExport implements FromView
{
public function view(): View
{
$gateways = Smsgateway::with('type:id,name')
->where('business_id', auth()->user()->business_id)
->when(request('channel'), function ($q) {
$q->where('channel', request('channel'));
})
->latest()
->get();
return view('marketingaddon::sms-gateways.excel-csv', [
'gateways' => $gateways
]);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Modules\MarketingAddon\App\Models\EmailSetting;
class AcnooEmailSettingController extends Controller
{
public function index()
{
$businessId = auth()->user()->business_id;
$email_setting = EmailSetting::where('business_id', $businessId)->first() ?? new EmailSetting();
return view('marketingaddon::email-settings.index', compact('email_setting'));
}
public function store(Request $request)
{
$request->validate([
'driver' => 'required|string|max:25|in:smtp,sendmail',
'host' => 'nullable|string|max:255',
'port' => 'nullable|numeric',
'username' => 'nullable|string|max:255',
'password' => 'nullable|string|max:255',
'encryption' => 'nullable|string|max:25',
'from_name' => 'nullable|string|max:255',
'from_address' => 'nullable|string|max:255',
]);
EmailSetting::updateOrCreate(
['business_id' => auth()->user()->business_id],
$request->only(['driver', 'host', 'port', 'username', 'password', 'encryption', 'from_name', 'from_address'])
);
return response()->json(__('Email setting updated successfully.'));
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Modules\MarketingAddon\App\Models\MessageReport;
class AcnooMessageLogController extends Controller
{
public function index(Request $request)
{
$message_logs = MessageReport::with('user:id,email')
->where('business_id', auth()->user()->business_id)
->when($request->search, function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('title', 'like', '%' . $request->search . '%')
->orWhere('channel', 'like', '%' . $request->search . '%')
->orWhere('phone', 'like', '%' . $request->search . '%')
->orWhere('status', 'like', '%' . $request->search . '%')
->orWhereHas('user', function ($q) use ($request) {
$q->where('email', 'like', '%' . $request->search . '%');
});
});
})
->when($request->type, function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('channel', $request->type);
});
})
->when($request->status, function ($q) use ($request) {
$q->where(function ($q) use ($request) {
$q->where('status', $request->status);
});
})
->latest()
->paginate($request->per_page ?? 20)
->appends($request->query());
if ($request->ajax()) {
return response()->json([
'data' => view('marketingaddon::message-logs.datas', compact('message_logs'))->render(),
]);
}
return view('marketingaddon::message-logs.index', compact('message_logs'));
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Maatwebsite\Excel\Facades\Excel;
use Modules\MarketingAddon\App\Exports\SmsGatewayExport;
use Modules\MarketingAddon\App\Models\GatewayType;
use Modules\MarketingAddon\App\Models\Smsgateway;
class AcnooSmsGatewayController extends Controller
{
public function index(Request $request)
{
$gateways = Smsgateway::with('type:id,name')
->where('business_id', auth()->user()->business_id)
->when($request->filled('channel'), function ($q) use ($request) {
$q->where('channel', $request->channel);
})
->when($request->filled('search'), function ($q) use ($request) {
$q->where(function ($query) use ($request) {
$query->where('name', 'like', '%' . $request->search . '%')
->orWhereHas('type', function ($type) use ($request) {
$type->where('name', 'like', '%' . $request->search . '%');
});
});
})
->latest()
->paginate($request->per_page ?? 20)
->appends($request->query());
if ($request->ajax()) {
return response()->json([
'data' => view('marketingaddon::sms-gateways.datas', compact('gateways'))->render()
]);
}
return view('marketingaddon::sms-gateways.index', compact('gateways'));
}
public function create(Request $request)
{
$types = GatewayType::where('channel', $request->channel)
->latest()
->get();
return view('marketingaddon::sms-gateways.create', compact('types'));
}
public function store(Request $request)
{
$request->validate([
'status' => 'required|integer',
'params' => 'required|array',
'params.*' => 'required|string',
'name' => [
'required',
Rule::unique('smsgateways')->where(function ($q) {
return $q->where('business_id', auth()->user()->business_id);
}),
],
'type_id' => 'required|exists:gateway_types,id',
]);
$type = GatewayType::findOrFail($request->type_id);
Smsgateway::create($request->all() + [
'driver' => $type->driver,
'namespace' => $type->namespace,
'business_id' => auth()->user()->business_id
]);
return response()->json([
'message' => __(':channel gateway created successfully.', ['channel' => ucfirst($request->channel)]),
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
]);
}
public function show(string $id)
{
$gateway = Smsgateway::findOrFail($id);
$gateway->update(['status' => !$gateway->status]);
return response()->json(['message' => 'Sms gateway']);
}
public function edit(Request $request, string $id)
{
$types = GatewayType::where('channel', $request->channel)->latest()->get();
$gateway = Smsgateway::where('business_id', auth()->user()->business_id)->findOrFail($id);
return view('marketingaddon::sms-gateways.edit', compact('gateway', 'types'));
}
public function update(Request $request, string $id)
{
$request->validate([
'status' => 'required|integer',
'params' => 'required|array',
'params.*' => 'required|string',
'type_id' => 'required|exists:gateway_types,id',
'name' => [
'required',
Rule::unique('smsgateways', 'name')->ignore($id)->where(function ($q) {
return $q->where('business_id', auth()->user()->business_id);
}),
],
]);
$type = GatewayType::findOrFail($request->type_id);
$gateway = Smsgateway::findOrFail($id);
$gateway->update($request->all() + [
'driver' => $type->driver,
'namespace' => $type->namespace
]);
return response()->json([
'message' => __(':channel gateway updated successfully.', ['channel' => ucfirst($request->channel)]),
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
]);
}
public function destroy(Request $request, string $id)
{
Smsgateway::where('id', $id)->delete();
return response()->json([
'message' => __(':channel gateway deleted successfully.', ['channel' => ucfirst($request->channel)]),
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
]);
}
public function deleteAll(Request $request)
{
Smsgateway::whereIn('id', $request->ids)->delete();
return response()->json([
'message' => __('Selected :channel gateway deleted successfully.', ['channel' => $request->channel]),
'redirect' => route('business.sms-gateways.index', ['channel' => $request->channel])
]);
}
public function exportExcel()
{
return Excel::download(new SmsGatewayExport, 'sms-gateway.xlsx');
}
public function exportCsv()
{
return Excel::download(new SmsGatewayExport, 'sms-gateway.csv');
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use App\Models\Option;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class AcnooSmsGatewaySettingController extends Controller
{
public function index()
{
$businessId = auth()->user()->business_id;
$sms_gateway_setting_key = 'sms-gateway-setting-' . $businessId;
$sms_gateway_setting = Option::where('key', $sms_gateway_setting_key)->first();
return view('marketingaddon::sms-gateway-settings.index', compact('sms_gateway_setting'));
}
public function store(Request $request)
{
$request->validate([
'sms_gateway_setting' => 'required|string|max:100|in:api,android',
]);
$key = 'sms-gateway-setting-' . auth()->user()->business_id;
Option::updateOrCreate(
['key' => $key],
['value' => $request->sms_gateway_setting]
);
Cache::forget($key);
return response()->json(__('SMS Gateway setting updated successfully.'));
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use App\Models\Option;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Modules\MarketingAddon\App\Models\Template;
class AcnooTemplateController extends Controller
{
public function index(Request $request)
{
$businessId = auth()->user()->business_id;
$type = $request->type;
$templates = Template::where('business_id', $businessId)
->where('type', $type)
->get()
->keyBy('title');
$page_data = Option::where('key', 'notification_settings_' . $businessId)->first()?->value ?? [];
return view('marketingaddon::templates.index', compact('templates', 'page_data'));
}
public function store(Request $request)
{
$businessId = auth()->user()->business_id;
$type = $request->type;
$tabs = ['new_sale', 'payment_received', 'payment_paid', 'purchase', 'payment_reminder', 'attendance'];
$request->validate(collect($tabs)
->flatMap(function ($key) use ($request, $type) {
if (!$request->has($key)) {
return [];
}
return [
"{$key}.template" => 'nullable|string|max:255',
"{$key}.subject" => $type === 'email' ? 'required|string|max:255' : 'nullable|string|max:255',
"{$key}.message" => 'required|string|max:1000',
];
})->toArray()
);
$optionKey = 'notification_settings_' . $businessId;
$option = Option::firstOrCreate(
['key' => $optionKey],
['value' => []]
);
$optionValue = $option->value ?? [];
foreach ($tabs as $tab) {
if (!$request->has($tab)) continue;
$tabData = $request->{$tab};
$optionValue[$tab][$type]['enabled'] = $tabData[$type]['enabled'] ?? 0;
$message = $tabData['message'] ?? null;
$variables = $message ? extractTemplateVariables($message) : [];
Template::updateOrCreate(
[
'business_id' => $businessId,
'title' => $tab,
'type' => $type,
],
[
'subject' => $tabData['subject'] ?? null,
'template' => $tabData['template'] ?? null,
'message' => $message,
'variables' => $variables,
]
);
}
$option->update(['value' => $optionValue]);
Cache::forget($optionKey);
return response()->json(__(ucfirst($type) . ' template updated successfully'));
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Modules\MarketingAddon\App\Http\Controllers;
use App\Models\Party;
use App\Http\Controllers\Controller;
class PaymentReminderController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(string $party_id)
{
$party = Party::with('business')->findOrFail($party_id);
$this->paymentReminderMessage($party->business, $party);
return redirect()->back()->with('message', 'Payment reminder sent successfully.');
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Modules\MarketingAddon\App\Jobs;
use Throwable;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Config;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Modules\MarketingAddon\App\Emails\DynamicMail;
use Modules\MarketingAddon\App\Models\EmailSetting;
use Modules\MarketingAddon\App\Services\SmsService;
use Modules\MarketingAddon\App\Models\MessageReport;
use Modules\MarketingAddon\App\Services\WhatsAppService;
class SendNotificationJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
protected string $channel,
protected object $template,
protected array $data,
protected int $notificationId,
protected int $businessId,
) {}
public function handle(
SmsService $sms,
WhatsAppService $whatsapp
): void {
try {
match ($this->channel) {
/** ===================== SMS ===================== */
'sms' => $sms->send(
phone: $this->data['phone'],
template: $this->template,
data: $this->data
),
/** ===================== WHATSAPP ===================== */
'whatsapp' => $whatsapp->send(
phone: $this->data['phone'],
template: $this->template,
businessId: $this->businessId,
name: $this->data['name'] ?? '',
data: $this->data
),
/** ===================== EMAIL ===================== */
'email' => $this->sendEmail(),
default => throw new \Exception('Invalid notification channel'),
};
MessageReport::where('id', $this->notificationId)->update([
'status' => 'success',
'send_at' => now(),
]);
} catch (Throwable $e) {
MessageReport::where('id', $this->notificationId)->update([
'status' => 'failed',
'send_at' => now(),
]);
throw $e;
}
}
/**
* Handle email sending with dynamic SMTP
*/
protected function sendEmail(): void
{
$smtp = EmailSetting::where('business_id', $this->businessId)->first();
if ($smtp) {
Config::set('mail.mailers.smtp.transport', $smtp->driver);
Config::set('mail.mailers.smtp.host', $smtp->host);
Config::set('mail.mailers.smtp.port', $smtp->port);
Config::set('mail.mailers.smtp.username', $smtp->username);
Config::set('mail.mailers.smtp.password', $smtp->password);
Config::set('mail.mailers.smtp.encryption', $smtp->encryption);
Config::set('mail.from.address', $smtp->from_address);
Config::set('mail.from.name', $smtp->from_name);
// IMPORTANT: reset mailer to avoid queue worker cache issues
Mail::forgetMailers();
Mail::to($this->data['email'])
->send(new DynamicMail($this->template, $this->data));
} else {
MessageReport::where('id', $this->notificationId)->update([
'status' => 'failed',
'send_at' => now(),
]);
}
}
/**
* Called automatically by Laravel after final failure
*/
public function failed(): void
{
MessageReport::where('id', $this->notificationId)->update([
'status' => 'failed',
'send_at' => now(),
]);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\MarketingAddon\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class EmailSetting extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'business_id',
'driver',
'host',
'port',
'username',
'password',
'encryption',
'from_name',
'from_address',
];
protected $casts = [
'business_id' => 'integer'
];
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\MarketingAddon\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class GatewayType extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'inputs',
'driver',
'channel',
'status',
'namespace',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'inputs' => 'json',
];
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Modules\MarketingAddon\App\Models;
use App\Models\Business;
use App\Models\User;
use App\Traits\ImageUrlTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MessageReport extends Model
{
use HasFactory, ImageUrlTrait;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title',
'business_id',
'template_id',
'smsgateway_id',
'user_id',
'channel',
'phone',
'message',
'attachment',
'send_at',
'status'
];
public function user()
{
return $this->belongsTo(User::class);
}
public function business()
{
return $this->belongsTo(Business::class);
}
public function smsgateway()
{
return $this->belongsTo(Smsgateway::class, 'smsgateway_id');
}
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'user_id' => 'integer',
'device_id' => 'integer',
'business_id' => 'integer',
'send_attempts' => 'integer',
];
protected $imageFields = ['attachment'];
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Modules\MarketingAddon\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Smsgateway extends Model
{
use HasFactory;
protected $fillable = [
'name',
'type_id',
'business_id',
'priority',
'status',
'params',
'namespace',
'driver',
'channel',
];
protected $casts = [
'params' => 'json',
];
public function type()
{
return $this->belongsTo(GatewayType::class, 'type_id');
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\MarketingAddon\App\Models;
use App\Traits\ImageUrlTrait;
use Illuminate\Database\Eloquent\Model;
class Template extends Model
{
use ImageUrlTrait;
protected $fillable = [
'business_id',
'title',
'template',
'type',
'attachment',
'subject',
'variables',
'message',
'status',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'variables' => 'array',
];
protected $imageFields = ['attachment'];
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Modules\MarketingAddon\App\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
class MarketingAddonServiceProvider extends ServiceProvider
{
protected string $moduleName = 'MarketingAddon';
protected string $moduleNameLower = 'marketingaddon';
/**
* 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->register(RouteServiceProvider::class);
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands(): void
{
//
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules(): void
{
//
}
/**
* 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.'\\'.config('modules.paths.generator.component-class.path'));
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
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,59 @@
<?php
namespace Modules\MarketingAddon\App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The module namespace to assume when generating URLs to actions.
*/
protected string $moduleNamespace = 'Modules\MarketingAddon\App\Http\Controllers';
/**
* 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')
->namespace($this->moduleNamespace)
->group(module_path('MarketingAddon', '/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace)
->group(module_path('MarketingAddon', '/routes/api.php'));
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Modules\MarketingAddon\App\Services;
use Modules\MarketingAddon\App\Models\Template;
use Modules\MarketingAddon\App\Models\MessageReport;
use Modules\MarketingAddon\App\Jobs\SendNotificationJob;
class NotifyService
{
public function send(string $event, array $data, string $businessId)
{
$settings = get_option('notification_settings_' . $businessId);
if (!isset($settings[$event])) {
return;
}
$array = [];
foreach (['sms', 'whatsapp', 'email'] as $channel) {
// Check enabled in settings
if (empty($settings[$event][$channel]['enabled'])) {
continue;
}
// Check required data per channel
if (in_array($channel, ['sms', 'whatsapp']) && empty($data['phone'])) {
continue;
}
if ($channel === 'email' && empty($data['email'])) {
continue;
}
$array[] = $channel;
$template = Template::where('business_id', $businessId)->where('title', $event)->where('type', $channel)->first();
if (empty($template)) {
continue;
}
$notification = MessageReport::create([
'title' => $event,
'channel' => $channel,
'business_id' => $businessId,
'template_id' => $template->id,
'message' => $template->message,
]);
// Dispatch job
SendNotificationJob::dispatch(
data: $data,
channel: $channel,
template: $template,
businessId: $businessId,
notificationId: $notification->id,
)->onQueue('high');
}
return $array;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Modules\MarketingAddon\App\Services;
use Exception;
use Modules\MarketingAddon\App\Models\Smsgateway;
class SmsService
{
public function send(string $phone, $template, array $data)
{
$gateways = Smsgateway::where('status', 1)->where('channel', 'sms')->where('business_id', $template->business_id)->get();
if (empty($gateways)) {
throw new \Exception('No active SMS gateway found for this business.');
}
$response = [
'status' => false,
'message' => 'Something went wrong, Please try again.'
];
$contents = $this->buildContent($template, $data);
foreach ($gateways as $gateway) {
$response = $gateway->namespace::send_message($gateway, $phone, $contents);
if (($response['status'] ?? false) === true) {
return;
}
}
throw new Exception('All SMS gateways failed');
}
protected function buildContent($template, array $data)
{
$message = $template->message;
foreach ($data as $key => $value) {
$message = str_replace('[' . $key . ']', $value, $message);
}
return $message;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Modules\MarketingAddon\App\Services;
use Modules\MarketingAddon\App\Models\Smsgateway;
class WhatsAppService
{
/**
* Send WhatsApp campaign via AiSensy
*/
public function send(
string $phone,
object $template,
int $businessId,
string $name,
array $data = [],
) {
$gateways = Smsgateway::where('channel', 'whatsapp')
->where('business_id', $businessId)
->where('status', 1)
->get();
if (empty($gateways)) {
throw new \Exception('No active WhatsApp gateway found for this business.');
}
foreach ($gateways as $gateway) {
$gateway->namespace::send_message($gateway, $phone, $template, $name, $data);
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\MarketingAddon\App\SmsGateways;
use Illuminate\Support\Facades\Http;
class AcnooSms
{
public static function send_message($gateway, $numbers, $contents)
{
$response = Http::post('https://acnoosms.com/send-message', [
'client_id' => $gateway->params['client_id'] ?? '',
'client_secret' => $gateway->params['client_secret'] ?? '',
'gateway_id' => $gateway->params['gateway_id'] ?? '',
'type' => 0,
'content' => $contents,
'numbers' => $numbers,
]);
if ($response['status']) {
return [
'status' => true,
];
}
return [
'status' => false,
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Modules\MarketingAddon\App\SmsGateways;
use Illuminate\Support\Facades\Http;
class HaDavar
{
public static function send_message($gateway, $numbers, $contents, $sender_name = null)
{
$response = Http::asForm()->{$gateway->params['api_method']}($gateway->params['api_url'], array_merge([
'Username' => $gateway->params['username'],
'Token' => $gateway->params['token'],
'Sender' => $sender_name ?? $gateway->params['sender'],
$gateway->params['number_key'] => $numbers,
$gateway->params['message_key'] => $contents,
]));
return $response->body();
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\MarketingAddon\App\SmsGateways;
use Illuminate\Support\Facades\Http;
class RouteMobile
{
public static function send_message($gateway, $numbers, $contents, $sender_name = null)
{
$response = Http::asForm()->{$gateway->params['api_method']}($gateway->params['api_url'], array_merge([
'dlr' => $gateway->params['dlr'],
'type' => $gateway->params['type'],
'source' => $sender_name ?? $gateway->params['source'],
$gateway->params['number_key'] => $numbers,
'username' => $gateway->params['username'],
'password' => $gateway->params['password'],
$gateway->params['message_key'] => $contents,
]));
$words = explode('|', $response);
$firstFourWords = array_slice($words, 0, 4);
if ($firstFourWords[0] == 1701) {
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Modules\MarketingAddon\App\SmsGateways;
class TzskSms
{
public static function send_message($gateway, $numbers, $contents)
{
$numbers = is_array($numbers) ? $numbers : explode(',', $numbers);
foreach ($gateway->params as $key => $param) {
config(['sms.drivers.' . $gateway->driver . '.' . $key => $param]);
}
sms()->via($gateway->driver)->send($contents, function ($sms) use ($numbers) {
$sms->to($numbers);
});
return true;
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Modules\MarketingAddon\App\WhatsappGateways;
use Illuminate\Support\Facades\Http;
class AiSensy
{
public static function send_message($gateway, $phone, $template, $name, $data)
{
$payload = [
'apiKey' => $gateway->params['api_key'],
'campaignName' => $template->template,
'destination' => $phone,
'userName' => $name,
'templateParams' => self::buildParams($template, $data),
'buttons' => self::aiSensyButtons($data['copy_text'] ?? ''),
'media' => self::buildMedia($template),
];
// 🔍 Log payload before sending
\Illuminate\Support\Facades\Log::info('AiSensy Payload', $payload);
$response = Http::withHeaders([
'Content-Type' => 'application/json',
])->post(
'https://backend.aisensy.com/campaign/t1/api/v2',
$payload
);
if ($response->successful()) {
return $response->json();
}
throw new \Exception(
'AiSensy Error: ' . $response->body()
);
}
/**
* Convert named data positional params (WhatsApp only)
*/
protected static function buildParams($template, array $data): array
{
if (empty($template->variables) || !is_array($template->variables)) {
return [];
}
$params = [];
foreach ($template->variables as $variable) {
// Skip button-related vars
if (in_array($variable, ['copy_text', 'expire_at'])) {
continue;
}
if (!isset($data[$variable])) {
continue;
}
$params[] = $data[$variable];
}
return $params;
}
protected static function aiSensyButtons(? string $copyText = null)
{
if (!$copyText) {
return [];
}
return [
[
'type' => 'button',
'sub_type' => 'url',
'index' => 0,
'parameters' => [
[
'type' => 'text',
'text' => $copyText ?? ''
]
]
]
];
}
protected static function buildMedia($template): ?array
{
if (empty($template?->attachment)) {
return null;
}
return [
'url' => asset($template->attachment),
'filename' => 'sample_media',
];
}
}

View File

@@ -0,0 +1,161 @@
<?php
namespace Modules\MarketingAddon\App\WhatsappGateways;
use Illuminate\Support\Facades\Http;
class WhatsAppCloudApi
{
public static function send_message($gateway, $phone, $template, $name, $data)
{
$payload = [
'messaging_product' => 'whatsapp',
'recipient_type' => 'individual',
'to' => $phone,
'type' => 'template',
'template' => [
'name' => $template->template,
'language' => [
'code' => $gateway->params['language_code'] ?? 'en_US',
],
],
];
$components = self::buildComponents($template, $data);
if (!empty($components)) {
$payload['template']['components'] = $components;
}
$apiVersion = $gateway->params['api_version'] ?? 'v25.0';
$phoneNumberId = $gateway->params['phone_number_id'];
$url = "https://graph.facebook.com/{$apiVersion}/{$phoneNumberId}/messages";
$response = Http::withToken($gateway->params['access_token'])
->withHeaders([
'Content-Type' => 'application/json',
])
->post($url, $payload);
if ($response->successful()) {
return $response->json();
}
throw new \Exception(
'WhatsApp Cloud API Error: ' . $response->body()
);
}
protected static function buildComponents($template, array $data): array
{
$components = [];
$header = self::buildHeader($template);
$body = self::buildBody($template, $data);
$button = self::buildButton($data['copy_text'] ?? null);
if (!empty($header)) {
$components[] = $header;
}
if (!empty($body)) {
$components[] = $body;
}
if (!empty($button)) {
$components[] = $button;
}
return $components;
}
protected static function buildBody($template, array $data): ?array
{
if (empty($template->variables) || !is_array($template->variables)) {
return null;
}
$parameters = [];
foreach ($template->variables as $variable) {
if (in_array($variable, ['copy_text', 'expire_at'])) {
continue;
}
if (!isset($data[$variable])) {
continue;
}
$parameters[] = [
'type' => 'text',
'text' => (string) $data[$variable],
];
}
if (empty($parameters)) {
return null;
}
return [
'type' => 'body',
'parameters' => $parameters,
];
}
protected static function buildHeader($template): ?array
{
if (empty($template?->attachment)) {
return null;
}
$mediaType = self::mediaType($template->attachment);
$media = ['link' => asset($template->attachment)];
if ($mediaType === 'document') {
$media['filename'] = basename($template->attachment);
}
return [
'type' => 'header',
'parameters' => [
[
'type' => $mediaType,
$mediaType => $media,
],
],
];
}
protected static function buildButton(?string $copyText = null): ?array
{
if (!$copyText) {
return null;
}
return [
'type' => 'button',
'sub_type' => 'url',
'index' => 0,
'parameters' => [
[
'type' => 'text',
'text' => $copyText,
],
],
];
}
protected static function mediaType(string $attachment): string
{
$extension = strtolower(pathinfo($attachment, PATHINFO_EXTENSION));
if (in_array($extension, ['jpg', 'jpeg', 'png', 'webp'])) {
return 'image';
}
if (in_array($extension, ['mp4', '3gp'])) {
return 'video';
}
return 'document';
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Modules\MarketingAddon\Database\Seeders;
use Illuminate\Database\Seeder;
use Modules\MarketingAddon\App\Models\GatewayType;
class GatewayTypeSeeder extends Seeder
{
public function run(): void
{
$gateway_types = array(
array('name' => 'AWS SNS', 'inputs' => '{"names":["key","secret","region","from","type"],"labels":["Api Key","Secret Key","Region","Sender ID","Type"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'sns', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Textlocal', 'inputs' => '{"names":["url","username","hash","from"],"labels":["Api URL","User Name","Hash","Sender Name"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'textlocal', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Twilio', 'inputs' => '{"names":["sid","token","from"],"labels":["Api SID","Api Token","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'twilio', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Clockwork', 'inputs' => '{"names":["key"],"labels":["Api Key"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'clockwork', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'LINK Mobility', 'inputs' => '{"names":["url","username","password","from"],"labels":["Api URL","User Name","Password","Sender name"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'linkmobility', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Melipayamak', 'inputs' => '{"names":["username","password","from","flash"],"labels":["User Name","Password","Default Number","Flash"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'melipayamak', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Melipayamakpattern', 'inputs' => '{"names":["username","password"],"labels":["User Name","Password"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'melipayamakpattern', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Kavenegar', 'inputs' => '{"names":["apiKey","from"],"labels":["Api Key","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'kavenegar', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Smsir', 'inputs' => '{"names":["url","apiKey","secretKey","from"],"labels":["Api URL","Api Key","Secret Key","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'smsir', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Tsms', 'inputs' => '{"names":["url","username","password","from"],"labels":["Api URL","User Name","Password","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'tsms', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Farazsms', 'inputs' => '{"names":["url","username","password","from"],"labels":["Api URL","User Name","Password","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'farazsms', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Farazsmspattern', 'inputs' => '{"names":["url","username","password","from"],"labels":["Api URL","User Name","Password","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'farazsmspattern', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'SMS Gateway Me', 'inputs' => '{"names":["apiToken","from"],"labels":["Api Token","Default Device ID"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'smsgatewayme', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'SmsGateWay24', 'inputs' => '{"names":["url","token","deviceid","from"],"labels":["Api URL","Api Token","Device ID","Device SIM Slot"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'smsgateway24', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Ghasedak', 'inputs' => '{"names":["url","apiKey","from"],"labels":["Api URL","Api Key","Default Number"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'ghasedak', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Sms77', 'inputs' => '{"names":["apiKey","flash","from"],"labels":["Api Key","Flash","Sender name"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'sms77', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'SabaPayamak', 'inputs' => '{"names":["url","username","password","from","token_valid_day"],"labels":["Api URL","User Name","Password","Default Number","Token Validity day"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'sabapayamak', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'LSim', 'inputs' => '{"names":["username","password","from"],"labels":["User Name","Password","Sender ID"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'lsim', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Rahyabcp', 'inputs' => '{"names":["url","username","password","from","flash"],"labels":["Api URL","User Name","Password","Default Number","Flash"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'rahyabcp', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Rahyabir', 'inputs' => '{"names":["url","username","password","company","from","token_valid_day"],"labels":["Api URL","User Name","Password","Company Name","Default Number","Token Validity Day"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'rahyabir', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'D7networks', 'inputs' => '{"names":["url","username","password","originator","report_url","token_valid_day"],"labels":["Api URL","User Name","Password","Originator","Report Url","Token Validity Day"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'd7networks', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Hamyarsms', 'inputs' => '{"names":["url","username","password","from","flash"],"labels":["Api URL","User Name","Password","Default Number","Flash"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'hamyarsms', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'SMSApi', 'inputs' => '{"names":["url","username","password","from","cc"],"labels":["Api URL","User Name","Password","Default Number","Country Code"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\TzskSms', 'driver' => 'smsapi', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Route Mobile', 'inputs' => '{"names":["api_url","api_method","username","password","type","dlr","number_key","destination","source","message_key"],"labels":["Api URL","Api Method","User Name","Password","Type","DLR","Number Key","Destination","Source","Message Key"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\RouteMobile', 'driver' => 'smsapi', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'HaDavar', 'inputs' => '{"names":["api_url","api_method","username","token","sender","number_key","message_key"],"labels":["Api URL","Api Method","User Name","Token","Sender","Number Key","Message Key"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\HaDavar', 'driver' => '', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'Acnoo Sms', 'inputs' => '{"names":["client_id","client_secret","gateway_id"],"labels":["Client Id","Client Secret","Gateway Id"]}', 'namespace' => 'Modules\MarketingAddon\App\SmsGateways\AcnooSms', 'driver' => 'acnoosms', 'channel' => 'sms', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
// whatsapp
array('name' => 'AiSensy', 'inputs' => '{"names":["api_key"],"labels":["Api Key"]}', 'namespace' => 'Modules\MarketingAddon\App\WhatsappGateways\AiSensy', 'driver' => 'aisensy', 'channel' => 'whatsapp', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
array('name' => 'WhatsApp Cloud API', 'inputs' => '{"names":["access_token","phone_number_id","api_version","language_code"],"labels":["Access Token","Phone Number ID","API Version","Template Language Code (en_US)"]}', 'namespace' => 'Modules\MarketingAddon\App\WhatsappGateways\WhatsAppCloudApi', 'driver' => 'whatsapp-cloud-api', 'channel' => 'whatsapp', 'status' => 1, 'created_at' => now(), 'updated_at' => now()),
);
GatewayType::insert($gateway_types);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Modules\MarketingAddon\Database\Seeders;
use Illuminate\Database\Seeder;
class MarketingAddonDatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
GatewayTypeSeeder::class,
TemplateSeeder::class,
OptionSeeder::class,
]);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Modules\MarketingAddon\Database\Seeders;
use App\Models\Option;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Artisan;
class OptionSeeder extends Seeder
{
public function run(): void
{
$options = array(
array('key' => 'notification_settings_1', 'value' => '{"new_sale":{"sms":{"enabled":1},"whatsapp":{"enabled":0},"email":{"enabled":0}},"payment_received":{"sms":{"enabled":0},"whatsapp":{"enabled":0},"email":{"enabled":1}},"payment_paid":{"sms":{"enabled":1},"whatsapp":{"enabled":0},"email":{"enabled":0}},"purchase":{"sms":{"enabled":1},"whatsapp":{"enabled":1},"email":{"enabled":1}},"payment_reminder":{"sms":{"enabled":0},"whatsapp":{"enabled":1},"email":{"enabled":0}}}', 'status' => '1', 'created_at' => '2026-01-08 03:35:24', 'updated_at' => '2026-01-10 07:26:48'),
array('key' => 'sms-gateway-setting-1', 'value' => '"api"', 'status' => '1', 'created_at' => now(), 'updated_at' => now()),
);
Option::insert($options);
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('route:clear');
Artisan::call('view:clear');
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Modules\MarketingAddon\Database\Seeders;
use Illuminate\Database\Seeder;
use Modules\MarketingAddon\App\Models\Template;
class TemplateSeeder extends Seeder
{
public function run(): void
{
$templates = array(
array('business_id' => '1', 'title' => 'new_sale', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","received_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Thanks for the purchase
Your Invoice amount: [amount]
Received: [received_amount] Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:01:51'),
array('business_id' => '1', 'title' => 'payment_received', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","received_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Thanks for the payment
Received amount: [received_amount]
Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:17:31'),
array('business_id' => '1', 'title' => 'payment_paid', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","paid_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Your Invoice amount: [amount]
Paid: [paid_amount] Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:14:47'),
array('business_id' => '1', 'title' => 'purchase', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["supplier_name","invoice_no","date"]', 'message' => 'Dear [supplier_name], a purchase order #[invoice_no] has been placed on [date].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'payment_reminder', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","business_phone"]', 'message' => 'Hello [customer_name], kindly clear your outstanding balance of [amount]. For details, contact us at [business_phone].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'attendance', 'type' => 'sms', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["employee_name","entry_time","exit_time","date","business_name"]', 'message' => 'Dear [employee_name], your entry time at [entry_time] and exit time at [exit_time] on [date]. Thanks, [business_name] HR.', 'status' => '1', 'created_at' => '2026-01-29 15:35:09', 'updated_at' => '2026-02-01 10:44:36'),
array('business_id' => '1', 'title' => 'new_sale', 'type' => 'whatsapp', 'template' => 'sell_confirmation', 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","received_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Thanks for the purchase
Your Invoice amount: [amount]
Received: [received_amount] Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:02:07'),
array('business_id' => '1', 'title' => 'payment_received', 'type' => 'whatsapp', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","received_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Thanks for the payment
Received amount: [received_amount]
Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:17:48'),
array('business_id' => '1', 'title' => 'payment_paid', 'type' => 'whatsapp', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","paid_amount","due_amount","invoice_url","business_name"]', 'message' => 'Dear [customer_name],
Your Invoice amount: [amount]
Paid: [paid_amount] Current Due: [due_amount]
Online invoice: [invoice_url]
Thank You
[business_name]', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:14:24'),
array('business_id' => '1', 'title' => 'purchase', 'type' => 'whatsapp', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["supplier_name","invoice_no","date"]', 'message' => 'Dear [supplier_name], a purchase order #[invoice_no] has been placed on [date].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'payment_reminder', 'type' => 'whatsapp', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["customer_name","amount","business_phone"]', 'message' => 'Hello [customer_name], kindly clear your outstanding balance of [amount]. For details, contact us at [business_phone].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'attendance', 'type' => 'whatsapp', 'template' => NULL, 'attachment' => NULL, 'subject' => NULL, 'variables' => '["employee_name","entry_time","exit_time","date","business_name"]', 'message' => 'Dear [employee_name], your entry time at [entry_time] and exit time at [exit_time] on [date]. Thanks, [business_name] HR.', 'status' => '1', 'created_at' => '2026-01-29 15:35:09', 'updated_at' => '2026-02-01 10:45:32'),
array('business_id' => '1', 'title' => 'new_sale', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'New Sale', 'variables' => '["customer_name","amount","received_amount","due_amount","invoice_url","business_name"]', 'message' => '<div>Dear [customer_name],</div><div>Thanks for the purchase</div><div>Your Invoice amount: [amount]</div><div>Received: [received_amount] Current Due: [due_amount]</div><div>Online invoice: [invoice_url]</div><div>Thank You</div><div>[business_name]</div>', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:02:32'),
array('business_id' => '1', 'title' => 'payment_received', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'Payment Received', 'variables' => '["customer_name","received_amount","due_amount","invoice_url","business_name"]', 'message' => '<div>Dear [customer_name],</div><div>Thanks for the payment&nbsp;</div><div>Received amount: [received_amount]</div><div>Current Due: [due_amount]</div><div>Online invoice: [invoice_url]</div><div>Thank You</div><div>[business_name]</div>', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:18:28'),
array('business_id' => '1', 'title' => 'payment_paid', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'Payment Paid', 'variables' => '["customer_name","amount","paid_amount","due_amount","invoice_url","business_name"]', 'message' => '<div>Dear [customer_name],</div><div>Your Invoice amount: [amount]</div><div>Paid: [paid_amount] Current Due: [due_amount]</div><div>Online invoice: [invoice_url]</div><div>Thank You</div><div>[business_name]</div>', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-02-01 11:14:14'),
array('business_id' => '1', 'title' => 'purchase', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'Purchase', 'variables' => '["supplier_name","invoice_no","date"]', 'message' => 'Dear [supplier_name], a purchase order #[invoice_no] has been placed on [date].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'payment_reminder', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'Payment Reminder', 'variables' => '["customer_name","amount","business_phone"]', 'message' => 'Hello [customer_name], kindly clear your outstanding balance of [amount]. For details, contact us at [business_phone].', 'status' => '1', 'created_at' => '2026-01-05 05:40:37', 'updated_at' => '2026-01-12 11:22:45'),
array('business_id' => '1', 'title' => 'attendance', 'type' => 'email', 'template' => NULL, 'attachment' => NULL, 'subject' => 'Attendance', 'variables' => '["employee_name","entry_time","exit_time","date","business_name"]', 'message' => '<span data-teams="true">Dear [employee_name], your entry time at [entry_time] and exit time at [exit_time] on [date]. Thanks, [business_name] HR.</span>', 'status' => '1', 'created_at' => '2026-01-29 15:35:09', 'updated_at' => '2026-02-01 10:46:41')
);
Template::insert($templates);
}
}

View File

@@ -0,0 +1,33 @@
<?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('gateway_types', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->longText('inputs');
$table->string('namespace')->nullable();
$table->string('driver')->nullable();
$table->boolean('status')->default(1);
$table->string('channel')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('gateway_types');
}
};

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('smsgateways', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('business_id')->constrained()->cascadeOnDelete();
$table->foreignId('type_id')->constrained('gateway_types')->cascadeOnDelete();
$table->boolean('status')->default(1);
$table->string('driver')->nullable();
$table->string('channel')->nullable();
$table->string('namespace')->nullable();
$table->longText('params')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('smsgateways');
}
};

View File

@@ -0,0 +1,36 @@
<?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->foreignId('business_id')->nullable()->constrained()->nullOnDelete();
$table->string('title');
$table->string('type'); // sms, whatsapp, email
$table->string('template')->nullable(); // for whatsapp only
$table->string('attachment')->nullable();
$table->string('subject')->nullable(); // for email only
$table->text('variables')->nullable();
$table->longText('message')->nullable();
$table->boolean('status')->default(1);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('templates');
}
};

View File

@@ -0,0 +1,38 @@
<?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('message_reports', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable();
$table->foreignId('business_id')->constrained()->cascadeOnDelete();
$table->foreignId('template_id')->nullable()->constrained()->nullOnDelete();
$table->foreignId('smsgateway_id')->nullable()->constrained()->nullOnDelete();
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete(); // Send To
$table->string('channel'); // sms, whatsapp, email
$table->string('phone')->nullable();
$table->longText('message')->nullable();
$table->string('attachment')->nullable();
$table->timestamp('send_at')->nullable();
$table->enum('status', ['pending', 'success', 'failed'])->default('pending');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('message_reports');
}
};

View File

@@ -0,0 +1,36 @@
<?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('email_settings', function (Blueprint $table) {
$table->id();
$table->foreignId('business_id')->constrained()->cascadeOnDelete();
$table->enum('driver', ['smtp', 'sendmail'])->default('smtp');
$table->string('host')->nullable();
$table->string('port')->nullable();
$table->string('username')->nullable();
$table->string('password')->nullable();
$table->string('encryption')->nullable();
$table->string('from_name')->nullable();
$table->string('from_address')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('email_settings');
}
};

View File

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

View File

View File

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

View File

@@ -0,0 +1,12 @@
{
"name": "MarketingAddon",
"alias": "marketingaddon",
"version": "1.2",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\MarketingAddon\\App\\Providers\\MarketingAddonServiceProvider"
],
"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,22 @@
<div class="modal fade" id="multi-delete-modal" tabindex="-1" aria-labelledby="multi-delete-modal-label" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="text-end">
<button type="button" class="btn-close m-3 mb-0" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body pt-0">
<div class="delete-modal">
<h5>{{ __('Are You Sure?') }}</h5>
<p>{{ __("You want to delete everything!") }}</p>
</div>
<div class="multiple-button-group">
<button class="btn reset-btn" data-bs-dismiss="modal">{{ __('Cancel') }}</button>
<form id="dynamic-delete-form" method="POST" class="ajaxform_instant_reload">
@csrf
<button class="btn theme-btn submit-btn create-all-delete" type="submit">{{ __('Yes, Delete It!') }}</button>
</form>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,69 @@
@extends('layouts.business.master')
@section('title')
{{ ('Email Settings') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card ">
<div class="card-bodys d-print-none">
<div class="table-header p-16">
<h4>{{ __('Email Settings') }}</h4>
</div>
<div class="order-form-section p-16">
<form action="{{ route('business.email-settings.store') }}" method="post" enctype="multipart/form-data" class="ajaxform">
@csrf
<div class="add-suplier-modal-wrapper">
<div class="row">
<div class="col-lg-4 mb-2">
<label>{{ __('Mail Driver') }}:</label>
<input type="text" name="driver" class="form-control" value="{{ $email_setting->driver }}" placeholder="{{ __('smtp') }}">
<small class="text-danger">{{ __('Supported: "smtp" and "sendmail" only') }}</small>
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('Host') }}:</label>
<input type="text" name="host" class="form-control" value="{{ $email_setting->host }}" placeholder="{{ __('mailtrap.io') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('Port') }}:</label>
<input type="number" name="port" class="form-control" value="{{ $email_setting->port }}" placeholder="{{ __('2525') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('Username') }}:</label>
<input type="text" name="username" class="form-control" value="{{ $email_setting->username }}" placeholder="{{ __('Enter username') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('Password') }}:</label>
<input type="text" name="password" class="form-control" value="{{ $email_setting->password }}" placeholder="{{ __('Enter password') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('Encryption') }}:</label>
<input type="text" name="encryption" class="form-control" value="{{ $email_setting->encryption }}" placeholder="{{ __('Enter encryption') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('From Name') }}:</label>
<input type="text" name="from_name" class="form-control" value="{{ $email_setting->from_name }}" placeholder="{{ __('Enter from name') }}">
</div>
<div class="col-lg-4 mb-2">
<label>{{ __('From Address') }}:</label>
<input type="text" name="from_address" class="form-control" value="{{ $email_setting->from_address }}" placeholder="{{ __('Enter from address') }}">
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="text-center mt-5">
<button class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,54 @@
<div class="responsive-table m-0">
<table class="table align-middle" id="erp-table">
<thead class="table-light">
<tr>
<th>{{ __('SL') }}.</th>
<th class="text-start">{{ __('Date & Time') }}</th>
<th class="text-start">{{ __('Title') }}</th>
<th class="text-start">{{ __('Message') }}</th>
<th class="text-start">{{ __('Message To') }}</th>
<th class="text-start">{{ __('Type') }}</th>
<th class="text-center">{{ __('Status') }}</th>
<th>{{ __('Action') }}</th>
</tr>
</thead>
<tbody>
@foreach ($message_logs as $message)
<tr>
<td>{{ $message_logs->firstItem() + $loop->index }}</td>
<td class="text-start">{{ formatted_date($message->send_at) }} {{ formatted_time($message->send_at) }}</td>
<td class="text-start">{{ $message->title }}</td>
<td class="text-start">{!! Str::words($message->message, 5, '...') !!}</td>
@if ($message->channel == 'email')
<td class="text-start">{{ $message->user?->email }}</td>
@else
<td class="text-start">{{ $message->phone }}</td>
@endif
<td class="text-start">{{ ucfirst($message->channel) }}</td>
<td class="text-center">
@if ($message->status == 'success')
<span class="success-badge">{{__('Success')}}</span>
@elseif ($message->status == 'failed')
<span class="failed-badge">{{__('Failed')}}</span>
@else
<span class="pending-badge">{{__('Pending')}}</span>
@endif
</td>
<td class="d-print-none">
<a href="#msg-log-view-modal" data-bs-toggle="modal" class="view-details-btn message-view-btn"
data-title="{{ $message->title }}"
data-message="{{ $message->message }}"
data-message-to="{{ $message->channel == 'email' ? $message->user?->email : $message->phone }}"
data-date="{{ formatted_date($message->send_at) }} {{ formatted_time($message->send_at) }}"
data-type="{{ ucfirst($message->channel) }}">
{{__('View Details')}}
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-3">
{{ $message_logs->links('vendor.pagination.bootstrap-5') }}
</div>

View File

@@ -0,0 +1,78 @@
@extends('layouts.business.master')
@section('title')
{{ __('Message Logs') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card">
<div class="card-bodys">
<div class="top-title smswhatsApp">
<h4>{{ __('SMS, Email, WhatsApp Log') }}</h4>
</div>
<div class="table-top-form p-16-0">
<form action="{{ route('business.message-logs.index') }}" method="GET" class="filter-form" table="#message-logs-data">
<div class="table-top-left d-flex gap-3 margin-l-16">
<div class="gpt-up-down-arrow position-relative">
<select name="per_page" class="form-control">
<option @selected(request('per_page') == 20) value="20">{{ __('Show- 20') }}</option>
<option @selected(request('per_page') == 50) value="50">{{ __('Show- 50') }}</option>
<option @selected(request('per_page') == 100) value="100">{{ __('Show- 100') }}</option>
<option @selected(request('per_page') == 500) value="500">{{ __('Show- 500') }}</option>
</select>
<span></span>
</div>
<div class="table-search position-relative">
<input class="form-control" type="text" name="search" placeholder="{{ __('Search...') }}" value="{{ request('search') }}">
<span class="position-absolute">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M14.582 14.582L18.332 18.332" stroke="#4D4D4D" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" />
<path d="M16.668 9.16797C16.668 5.02584 13.3101 1.66797 9.16797 1.66797C5.02584 1.66797 1.66797 5.02584 1.66797 9.16797C1.66797 13.3101 5.02584 16.668 9.16797 16.668C13.3101 16.668 16.668 13.3101 16.668 9.16797Z" stroke="#4D4D4D" stroke-width="1.25" stroke-linejoin="round" />
</svg>
</span>
</div>
<div class="table-search position-relative w-auto">
<div class="gpt-up-down-arrow position-relative">
<select name="type" class="form-control">
<option value="">{{ __('All Type') }}</option>
<option value="sms">{{ __('SMS') }}</option>
<option value="whatsapp">{{ __('WhatsApp') }}</option>
<option value="email">{{ __('Email') }}</option>
</select>
<span></span>
</div>
</div>
<div class="table-search position-relative w-auto">
<div class="gpt-up-down-arrow position-relative">
<select name="status" class="form-control">
<option value="">{{ __('All Status') }}</option>
<option value="pending">{{ __('Pending') }}</option>
<option value="success">{{ __('Success') }}</option>
<option value="failed">{{ __('Failed') }}</option>
</select>
<span></span>
</div>
</div>
</div>
</form>
</div>
<div id="message-logs-data">
@include('marketingaddon::message-logs.datas')
</div>
</div>
</div>
</div>
</div>
@endsection
@push('modal')
@include('marketingaddon::message-logs.view')
@endpush

View File

@@ -0,0 +1,44 @@
<div class="modal fade p-0" id="msg-log-view-modal">
<div class="modal-dialog modal-dialog-centered modal-md">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">{{ __('View Details') }}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body order-form-section">
<table class="info-table">
<tbody>
<tr>
<td class="fw-bold" id="title"></td>
</tr>
<tr>
<td class="text-muted" id="message"></td>
</tr>
<tr>
<td>
<svg class="me-1" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 1.33203V3.33203M4 1.33203V3.33203" stroke="#3F3F46" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.0003 2.33203H3.00033C2.26395 2.33203 1.66699 2.92898 1.66699 3.66536V13.332C1.66699 14.0684 2.26395 14.6654 3.00033 14.6654H13.0003C13.7367 14.6654 14.3337 14.0684 14.3337 13.332V3.66536C14.3337 2.92898 13.7367 2.33203 13.0003 2.33203Z" stroke="#3F3F46" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1.66699 5.66797H14.3337" stroke="#3F3F46" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.99667 8.66797H8.00267M7.99667 11.3346H8.00267M10.6603 8.66797H10.6663M5.33301 8.66797H5.33899M5.33301 11.3346H5.33899" stroke="#3F3F46" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>{{ __('Date') }}:
<span class="text-muted" id="date"></span>
</td>
</tr>
<tr>
<td class="border-0 w-50 viewmodal-type">
{{ __('Type') }}:
<span class="text-muted" id="type"></span>
</td>
<td class="border-0 w-50 text-end viewmodal-type">
{{ __('Message To') }}:
<span class="text-muted" id="message_to"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
@extends('layouts.business.master')
@section('title')
{{ ('SMS Gateway Settings') }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card ">
<div class="card-bodys d-print-none">
<div class="table-header p-16">
<h4>{{ __('SMS Gateway Settings') }}</h4>
</div>
<div class="order-form-section p-16">
<form action="{{ route('business.sms-gateway-settings.store') }}" method="post" enctype="multipart/form-data" class="ajaxform">
@csrf
<div class="add-suplier-modal-wrapper">
<div class="row">
<div class="col-lg-12 mb-2">
<label>{{ __('Select SMS Gateway') }}</label>
<div class="gpt-up-down-arrow position-relative">
<select name="sms_gateway_setting" class="form-control select-dropdown">
<option value="api" @selected(optional($sms_gateway_setting)->value == 'api')>{{ __('Api Gateway') }}</option>
</select>
<span></span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="button-group text-center mt-5">
<button type="reset" class="theme-btn border-btn m-2">{{__('Reset')}}</button>
<button class="theme-btn m-2 submit-btn">{{__('Save')}}</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,72 @@
@extends('layouts.business.master')
@section('title')
{{ __('Add :channel Gateway', ['channel' => __(ucfirst(request('channel')))]) }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card border-0">
<div class="card-bodys ">
<div class="table-header p-16">
<h4>{{ __('Add :channel Gateway', ['channel' => __(ucfirst(request('channel')))]) }}</h4>
<a href="{{ route('business.sms-gateways.index', ['channel' => request('channel')]) }}" class="add-order-btn rounded-2">
<i class="far fa-list" aria-hidden="true"></i>{{ __(':channel Gateway List', ['channel' => __(ucfirst(request('channel')))]) }}
</a>
</div>
<div class="order-form-section p-16">
<form action="{{ route('business.sms-gateways.store') }}" method="post" enctype="multipart/form-data" class="ajaxform_instant_reload">
@csrf
<input type="hidden" name="channel" value="{{ request('channel') }}">
<div class="add-suplier-modal-wrapper">
<div class="row" id="input-container">
<div class="col-sm-6 mb-2">
<label>{{ __('Gateway Name') }}</label>
<input type="text" name="name" required class="form-control" placeholder="{{ __('Enter gateway name') }}">
</div>
<div class="col-sm-6 mb-2">
<label>{{ __('Gateway Type') }}</label>
<div class="gpt-up-down-arrow position-relative">
<select name="type_id" required="" class="form-control select-dropdown gateway-type">
<option value="1">{{ __('Select a type') }}</option>
@foreach ($types as $type)
<option data-names='@json($type->inputs['names'] ?? [])' data-labels='@json($type->inputs['labels'] ?? [])' value="{{ $type->id }}">{{ $type->name }}</option>
@endforeach
</select>
<span></span>
</div>
</div>
<div class="col-sm-6 mb-2">
<label>{{ __('Status') }}</label>
<div class="gpt-up-down-arrow position-relative">
<select name="status" required="" class="form-control select-dropdown">
<option value="1">{{ __('Active') }}</option>
<option value="0">{{ __('Deactive') }}</option>
</select>
<span></span>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="button-group text-center mt-5">
<button type="reset" class="theme-btn border-btn m-2">{{__('Reset')}}</button>
<button class="theme-btn m-2 submit-btn">{{__('Save')}}</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,64 @@
<div class="responsive-table m-0">
<table class="table" id="datatable">
<thead>
<tr>
<th class="w-60 d-print-none">
<div class="d-flex align-items-center gap-3">
<input type="checkbox" class="select-all-delete multi-delete">
</div>
</th>
<th>{{ __('SL') }}.</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Gateway Type') }}</th>
<th class="d-print-none">{{ __('Status') }}</th>
<th class="d-print-none">{{ __('Action') }}</th>
</tr>
</thead>
<tbody>
@foreach ($gateways as $gateway)
<tr>
<td class="w-60 checkbox d-print-none">
<input type="checkbox" name="ids[]" class="delete-checkbox-item multi-delete"
value="{{ $gateway->id }}">
</td>
<td>{{ $gateways->firstItem() + $loop->index }}</td>
<td>{{ $gateway->name }}</td>
<td>{{ $gateway->type->name ?? '' }}</td>
<td class="text-center w-150 d-print-none">
<label class="switch">
<input type="checkbox" {{ $gateway->status == 1 ? 'checked' : '' }} class="status"
data-url="{{ route('business.sms-gateways.show', $gateway->id) }}" data-method="GET">
<span class="slider round"></span>
</label>
</td>
<td class="d-print-none">
<div class="dropdown table-action">
<button type="button" data-bs-toggle="dropdown">
<i class="far fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li>
<a href="{{ route('business.sms-gateways.edit', ['sms_gateway' => $gateway->id, 'channel' => request('channel')]) }}"><i
class="fal fa-edit"></i>
{{ __('Edit') }}
</a>
</li>
<li>
<a href="{{ route('business.sms-gateways.destroy', ['sms_gateway' => $gateway->id, 'channel' => request('channel')]) }}"
class="confirm-action" data-method="DELETE">
<i class="fal fa-trash-alt"></i>
{{ __('Delete') }}
</a>
</li>
</ul>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="mt-3 d-print-none">
{{ $gateways->links('vendor.pagination.bootstrap-5') }}
</div>

View File

@@ -0,0 +1,79 @@
@extends('layouts.business.master')
@section('title')
{{ __('Edit :channel Gateway', ['channel' => __(ucfirst(request('channel')))]) }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card border-0">
<div class="card-bodys ">
<div class="table-header p-16">
<h4>{{ __('Edit :channel Gateway', ['channel' => __(ucfirst(request('channel')))]) }}</h4>
<a href="{{ route('business.sms-gateways.index', ['channel' => request('channel')]) }}" class="add-order-btn rounded-2">
<i class="far fa-list" aria-hidden="true"></i>{{ __(':channel Gateway List', ['channel' => __(ucfirst(request('channel')))]) }}
</a>
</div>
<div class="order-form-section p-16">
<form action="{{ route('business.sms-gateways.update', $gateway->id) }}" method="post" class="ajaxform_instant_reload">
@csrf
@method('put')
<input type="hidden" name="channel" value="{{ $gateway->channel }}">
<div class="add-suplier-modal-wrapper">
<div class="row" id="input-container">
<div class="col-sm-6 mb-2">
<label>{{ __('Gateway Name') }}</label>
<input type="text" name="name" value="{{ $gateway->name }}" class="form-control" placeholder="{{ __('Enter gateway name') }}">
</div>
<div class="col-sm-6 mb-2">
<label>{{ __('Gateway Type') }}</label>
<div class="gpt-up-down-arrow position-relative">
<select name="type_id" required="" class="form-control select-dropdown gateway-type">
<option value="1">{{ __('Select a type') }}</option>
@foreach ($types as $type)
<option @selected($type->id == $gateway->type_id) data-names='@json($type->inputs['names'] ?? [])' data-labels='@json($type->inputs['labels'] ?? [])' value="{{ $type->id }}">{{ $type->name }}</option>
@endforeach
</select>
<span></span>
</div>
</div>
<div class="col-sm-6 mb-2">
<label>{{ __('Status') }}</label>
<div class="gpt-up-down-arrow position-relative">
<select name="status" required="" class="form-control select-dropdown">
<option @selected($gateway->status == 1) value="1">{{ __('Active') }}</option>
<option @selected($gateway->status == 0) value="0">{{ __('Deactive') }}</option>
</select>
<span></span>
</div>
</div>
@foreach ($gateway->params ?? [] as $key => $param)
<div class="col-sm-6 mb-2 removable-field">
<label>{{ ucfirst($key) }}</label>
<input type="text" name="params[{{ $key }}]" value="{{ $param }}" class="form-control" placeholder="{{ ucfirst($key) }}">
</div>
@endforeach
</div>
<div class="row">
<div class="col-lg-12">
<div class="button-group text-center mt-5">
<button type="reset" class="theme-btn border-btn m-2">{{ __('Reset') }}</button>
<button class="theme-btn m-2 submit-btn">{{ __('Save') }}</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th>{{ __('SL') }}.</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Gateway Type') }}</th>
<th>{{ __('Status') }}</th>
</tr>
</thead>
<tbody>
@foreach ($gateways as $gateway)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $gateway->name }}</td>
<td>{{ $gateway->type->name ?? '' }}</td>
<td>{{ $gateway->status == 1 ? 'Active' : 'Deactive' }}</td>
</tr>
@endforeach
</tbody>
</table>

View File

@@ -0,0 +1,92 @@
@extends('layouts.business.master')
@section('title')
{{ __(':channel Gateway List', ['channel' => __(ucfirst(request('channel')))]) }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card ">
<div class="card-bodys d-print-none">
<div class="table-header p-16">
<h4>{{ __(':channel Gateway List', ['channel' => __(ucfirst(request('channel')))]) }}</h4>
<a type="button" href="{{ route('business.sms-gateways.create', ['channel' => request('channel')]) }}" class="add-order-btn rounded-2">
<i class="fas fa-plus-circle me-1"></i>{{ __('Add New') }}
</a>
</div>
<div class="table-top-form p-16-0">
<form action="{{ route('business.sms-gateways.index') }}" method="GET" class="filter-form" table="#sms-gateways-data">
<input type="hidden" name="channel" value="{{ request('channel') }}">
<div class="table-top-left d-flex gap-3 margin-l-16">
<div class="gpt-up-down-arrow position-relative">
<select name="per_page" class="form-control">
<option @selected(request('per_page') == 20) value="20">{{ __('Show 20') }}</option>
<option @selected(request('per_page') == 50) value="50">{{ __('Show 50') }}</option>
<option @selected(request('per_page') == 100) value="100">{{ __('Show 100') }}</option>
<option @selected(request('per_page') == 500) value="500">{{ __('Show 500') }}</option>
</select>
<span></span>
</div>
<div class="table-search position-relative">
<input class="form-control searchInput" type="text" name="search" placeholder="{{ __('Search...') }}" value="{{ request('search') }}">
<span class="position-absolute">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.582 14.582L18.332 18.332" stroke="#4D4D4D" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16.668 9.16797C16.668 5.02584 13.3101 1.66797 9.16797 1.66797C5.02584 1.66797 1.66797 5.02584 1.66797 9.16797C1.66797 13.3101 5.02584 16.668 9.16797 16.668C13.3101 16.668 16.668 13.3101 16.668 9.16797Z" stroke="#4D4D4D" stroke-width="1.25" stroke-linejoin="round"/>
</svg>
</span>
</div>
</div>
</form>
<div class="table-top-btn-group">
<ul>
<li>
<a href="{{ route('business.sms-gateways.csv', ['channel' => request('channel')]) }}">
<img src="{{ asset('assets/images/logo/csv.svg') }}" alt="">
</a>
</li>
<li>
<a href="{{ route('business.sms-gateways.excel', ['channel' => request('channel')]) }}">
<img src="{{ asset('assets/images/logo/excel.svg') }}" alt="">
</a>
</li>
<li>
<a onclick="window.print()" class="print-window">
<img src="{{ asset('assets/images/logo/printer.svg') }}" alt="">
</a>
</li>
</ul>
</div>
</div>
</div>
<div class="delete-item delete-show d-none">
<div class="delete-item-show">
<p class="fw-bold"><span class="selected-count"></span> {{ __('items show') }}</p>
<button data-bs-toggle="modal" class="trigger-modal" data-bs-target="#multi-delete-modal" data-url="{{ route('business.sms-gateways.delete-all', ['channel' => request('channel')]) }}">{{ __('Delete') }}</button>
</div>
</div>
<div id="sms-gateways-data">
@include('marketingaddon::sms-gateways.datas')
</div>
</div>
</div>
</div>
@endsection
@push('modal')
@include('marketingaddon::component.delete-modal')
@endpush

View File

@@ -0,0 +1,29 @@
{{-- hidden input for all tab --}}
<input type="hidden" name="type" value="{{ request('type') }}">
<div class="col-lg-4">
<div class="row">
<div class="col-12">
<h4 class="available-tags-title">{{__('Available Tags')}}</h4>
<div class="tags-card">
<ul class="tags-list">
<li><span>{{__('Customer Name')}}</span><span class="text-primary placeholder-btn" data-value="[customer_name]"> [customer_name]</span></li>
<li><span>{{__('Employee Name')}}</span><span class="text-primary placeholder-btn" data-value="[employee_name]"> [employee_name]</span></li>
<li><span>{{__('Invoice No')}}</span><span class="text-primary placeholder-btn" data-value="[invoice_no]"> [invoice_no]</span></li>
<li><span>{{__('Invoice Url')}}</span><span class="text-primary placeholder-btn" data-value="[invoice_url]"> [invoice_url]</span></li>
<li><span>{{__('Amount')}}</span><span class="text-primary placeholder-btn" data-value="[amount]"> [amount]</span></li>
<li><span>{{__('Date')}}</span><span class="text-primary placeholder-btn" data-value="[date]"> [date]</span></li>
<li><span>{{__('Time')}}</span><span class="text-primary placeholder-btn" data-value="[time]"> [time]</span></li>
<li><span>{{__('Business Name')}}</span><span class="text-primary placeholder-btn" data-value="[business_name]"> [business_name]</span></li>
<li><span>{{__('Business Phone')}}</span><span class="text-primary placeholder-btn" data-value="[business_phone]"> [business_phone]</span></li>
<li><span>{{__('Received Amount')}}</span><span class="text-primary placeholder-btn" data-value="[received_amount]"> [received_amount]</span></li>
<li><span>{{__('Paid Amount')}}</span><span class="text-primary placeholder-btn" data-value="[paid_amount]"> [paid_amount]</span></li>
<li><span>{{__('Due Amount')}}</span><span class="text-primary placeholder-btn" data-value="[due_amount]"> [due_amount]</span></li>
<li><span>{{__('Entry Time')}}</span><span class="text-primary placeholder-btn" data-value="[entry_time]"> [entry_time]</span></li>
<li><span>{{__('Exit Time')}}</span><span class="text-primary placeholder-btn" data-value="[exit_time]"> [exit_time]</span></li>
</ul>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,380 @@
@extends('layouts.business.master')
@section('title')
{{ __(':type Template', ['type' => __(__(ucfirst(request('type'))))]) }}
@endsection
@section('main_content')
<div class="erp-table-section">
<div class="container-fluid">
<div class="card">
<div class="card-bodys">
<div class="table-header p-16">
<h4>{{ __(':type Template', ['type' => __(ucfirst(request('type')))]) }}</h4>
</div>
</div>
<ul class="nav nav-tabs" id="settingsTab" role="tablist">
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link active" id="newsale-tab" data-bs-toggle="tab" data-bs-target="#newsale" type="button" role="tab">
{{__('New Sale')}}
</button>
</li>
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link" id="paymentreceived-tab" data-bs-toggle="tab" data-bs-target="#paymentreceived" type="button" role="tab">
{{__('Payment Received')}}
</button>
</li>
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link" id="paymentpaid-tab" data-bs-toggle="tab"
data-bs-target="#paymentpaid" type="button" role="tab">
{{__('Payment Paid')}}
</button>
</li>
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link" id="purchase-tab" data-bs-toggle="tab" data-bs-target="#purchase" type="button" role="tab">
{{__('Purchase')}}
</button>
</li>
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link" id="paymentreminder-tab" data-bs-toggle="tab" data-bs-target="#paymentreminder" type="button" role="tab">
{{__('Payment Reminder')}}
</button>
</li>
@if (moduleCheck('HrmAddon'))
<li class="nav-item settings-item" role="presentation">
<button class="nav-link settings-link" id="attendance-tab" data-bs-toggle="tab" data-bs-target="#attendance" type="button" role="tab">
{{__('Attendance')}}
</button>
</li>
@endif
</ul>
<div class="tab-content mt-3" id="settingsTabContent">
<div class="tab-pane fade show active" id="newsale" role="tabpanel" aria-labelledby="newsale-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="new_sale[subject]" value="{{ $templates['new_sale']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="new_sale[template]" value="{{ $templates['new_sale']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="new_sale[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['new_sale']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="new_sale[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['new_sale']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="new_sale[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['new_sale'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_sale" name="new_sale[{{ request('type') }}][enabled]" value="1">
<label for="status_for_sale">{{__('Enable Sale :type Notification', ['type' => __(ucfirst(request('type')))])}}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="paymentreceived" role="tabpanel" aria-labelledby="paymentreceived-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="payment_received[subject]" value="{{ $templates['payment_received']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="payment_received[template]" value="{{ $templates['payment_received']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="payment_received[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_received']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="payment_received[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_received']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="payment_received[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['payment_received'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_payment_received" name="payment_received[{{ request('type') }}][enabled]" value="1">
<label for="status_for_payment_received">{{__('Enable Payment Received :type Notification', ['type' => __(ucfirst(request('type')))])}}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="paymentpaid" role="tabpanel" aria-labelledby="paymentpaid-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="payment_paid[subject]" value="{{ $templates['payment_paid']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="payment_paid[template]" value="{{ $templates['payment_paid']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="payment_paid[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_paid']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="payment_paid[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_paid']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="payment_paid[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['payment_paid'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_payment_paid" name="payment_paid[{{ request('type') }}][enabled]" value="1">
<label for="status_for_payment_paid">{{__('Enable Payment Paid :type Notification', ['type' => __(ucfirst(request('type')))])}}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="purchase" role="tabpanel" aria-labelledby="purchase-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="purchase[subject]" value="{{ $templates['purchase']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="purchase[template]" value="{{ $templates['purchase']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="purchase[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['purchase']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="purchase[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['purchase']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="purchase[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['purchase'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_purchase" name="purchase[{{ request('type') }}][enabled]" value="1">
<label for="status_for_purchase">{{__('Enable Purchase :type Notification', ['type' => __(ucfirst(request('type')))])}}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
<div class="tab-pane fade" id="paymentreminder" role="tabpanel" aria-labelledby="paymentreminder-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="payment_reminder[subject]" value="{{ $templates['payment_reminder']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="payment_reminder[template]" value="{{ $templates['payment_reminder']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="payment_reminder[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_reminder']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="payment_reminder[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['payment_reminder']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="payment_reminder[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['payment_reminder'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_payment_reminder" name="payment_reminder[{{ request('type') }}][enabled]" value="1">
<label for="status_for_payment_reminder">{{ __('Enable Payment Reminder :type Notification', ['type' => __(ucfirst(request('type')))]) }}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
@if (moduleCheck('HrmAddon'))
<div class="tab-pane fade" id="attendance" role="tabpanel" aria-labelledby="attendance-tab">
<div class="p-3">
<form action="{{ route('business.templates.store') }}" method="POST" class="ajaxform">
@csrf
<div class="row">
<div class="col-lg-8">
<div class="row">
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label class="form-label">{{__('Subject')}}</label>
<input type="text" class="form-control" name="attendance[subject]" value="{{ $templates['attendance']->subject ?? '' }}" placeholder="Enter subject">
</div>
@elseif (request('type') == 'whatsapp')
<div class="mb-3 col-12">
<label class="form-label">{{__('Template')}}</label>
<input type="text" class="form-control" name="attendance[template]" value="{{ $templates['attendance']->template ?? '' }}" placeholder="Enter template">
</div>
@endif
@if (request('type') == 'email')
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body summernote-template message-box" name="attendance[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['attendance']->message ?? '' }}</textarea>
</div>
@else
<div class="mb-3 col-12">
<label for="message" class="form-label">{{__('Message')}}</label>
<textarea class="form-control w-100 sms-body message-box" name="attendance[message]" rows="4" cols="12" placeholder="Enter your message..." required>{{ $templates['attendance']->message ?? '' }}</textarea>
</div>
@endif
<div class="mb-3 col-12">
<div class="d-flex align-items-center gap-3">
<input type="hidden" name="attendance[{{ request('type') }}][enabled]" value="0">
<input @checked($page_data['attendance'][request('type')]['enabled'] ?? false) type="checkbox" class="multi-delete" id="status_for_attendance" name="attendance[{{ request('type') }}][enabled]" value="1">
<label for="status_for_attendance">{{ __('Enable Attendance :type Notification', ['type' => __(ucfirst(request('type')))]) }}</label>
</div>
</div>
</div>
</div>
@include('marketingaddon::templates.avaiable-tags')
<div class="d-flex align-items-center justify-content-center mt-4">
<button type="submit" class="theme-btn m-2 submit-btn">{{__('Submit')}}</button>
</div>
</div>
</form>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/summernote-lite.js') }}"></script>
@endpush

View File

View File

@@ -0,0 +1 @@
<?php

View File

@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\MarketingAddon\App\Http\Controllers as Business;
Route::group(['domain' => request()->getHost(), 'as' => 'business.', 'prefix' => 'business', 'middleware' => ['users', 'expired']], function () {
Route::resource('message-logs', Business\AcnooMessageLogController::class)->only('index');
Route::resource('templates', Business\AcnooTemplateController::class)->only('index', 'store');
Route::get('payment-reminder/{party_id}', [Business\PaymentReminderController::class, 'index'])->name('payment-reminder');
Route::resource('sms-gateways', Business\AcnooSmsGatewayController::class);
Route::post('sms-gateways/delete-all', [Business\AcnooSmsGatewayController::class, 'deleteAll'])->name('sms-gateways.delete-all');
Route::post('sms-gateways/filter', [Business\AcnooSmsGatewayController::class, 'acnooFilter'])->name('sms-gateways.filter');
Route::get('sms-gateways-excel', [Business\AcnooSmsGatewayController::class, 'exportExcel'])->name('sms-gateways.excel');
Route::get('sms-gateways-csv', [Business\AcnooSmsGatewayController::class, 'exportCsv'])->name('sms-gateways.csv');
Route::resource('sms-gateway-settings', Business\AcnooSmsGatewaySettingController::class)->only('index', 'store');
Route::resource('email-settings', Business\AcnooEmailSettingController::class)->only('index', 'store');
});

View File

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