update marketing
All checks were successful
All checks were successful
This commit is contained in:
62
Modules/MarketingAddon/App/Emails/DynamicMail.php
Normal file
62
Modules/MarketingAddon/App/Emails/DynamicMail.php
Normal 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;
|
||||
}
|
||||
}
|
||||
26
Modules/MarketingAddon/App/Exports/SmsGatewayExport.php
Normal file
26
Modules/MarketingAddon/App/Exports/SmsGatewayExport.php
Normal 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
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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.'));
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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.'));
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
121
Modules/MarketingAddon/App/Jobs/SendNotificationJob.php
Normal file
121
Modules/MarketingAddon/App/Jobs/SendNotificationJob.php
Normal 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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/Models/EmailSetting.php
Normal file
30
Modules/MarketingAddon/App/Models/EmailSetting.php
Normal 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'
|
||||
];
|
||||
}
|
||||
34
Modules/MarketingAddon/App/Models/GatewayType.php
Normal file
34
Modules/MarketingAddon/App/Models/GatewayType.php
Normal 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',
|
||||
];
|
||||
}
|
||||
63
Modules/MarketingAddon/App/Models/MessageReport.php
Normal file
63
Modules/MarketingAddon/App/Models/MessageReport.php
Normal 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'];
|
||||
|
||||
}
|
||||
32
Modules/MarketingAddon/App/Models/Smsgateway.php
Normal file
32
Modules/MarketingAddon/App/Models/Smsgateway.php
Normal 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');
|
||||
}
|
||||
}
|
||||
34
Modules/MarketingAddon/App/Models/Template.php
Normal file
34
Modules/MarketingAddon/App/Models/Template.php
Normal 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'];
|
||||
}
|
||||
0
Modules/MarketingAddon/App/Providers/.gitkeep
Normal file
0
Modules/MarketingAddon/App/Providers/.gitkeep
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
64
Modules/MarketingAddon/App/Services/NotifyService.php
Normal file
64
Modules/MarketingAddon/App/Services/NotifyService.php
Normal 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;
|
||||
}
|
||||
}
|
||||
46
Modules/MarketingAddon/App/Services/SmsService.php
Normal file
46
Modules/MarketingAddon/App/Services/SmsService.php
Normal 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;
|
||||
}
|
||||
}
|
||||
33
Modules/MarketingAddon/App/Services/WhatsAppService.php
Normal file
33
Modules/MarketingAddon/App/Services/WhatsAppService.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/SmsGateways/AcnooSms.php
Normal file
30
Modules/MarketingAddon/App/SmsGateways/AcnooSms.php
Normal 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,
|
||||
];
|
||||
}
|
||||
}
|
||||
21
Modules/MarketingAddon/App/SmsGateways/HaDavar.php
Normal file
21
Modules/MarketingAddon/App/SmsGateways/HaDavar.php
Normal 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();
|
||||
}
|
||||
}
|
||||
30
Modules/MarketingAddon/App/SmsGateways/RouteMobile.php
Normal file
30
Modules/MarketingAddon/App/SmsGateways/RouteMobile.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Modules/MarketingAddon/App/SmsGateways/TzskSms.php
Normal file
21
Modules/MarketingAddon/App/SmsGateways/TzskSms.php
Normal 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;
|
||||
}
|
||||
}
|
||||
99
Modules/MarketingAddon/App/WhatsappGateways/AiSensy.php
Normal file
99
Modules/MarketingAddon/App/WhatsappGateways/AiSensy.php
Normal 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',
|
||||
];
|
||||
}
|
||||
}
|
||||
161
Modules/MarketingAddon/App/WhatsappGateways/WhatsAppCloudApi.php
Normal file
161
Modules/MarketingAddon/App/WhatsappGateways/WhatsAppCloudApi.php
Normal 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';
|
||||
}
|
||||
}
|
||||
0
Modules/MarketingAddon/Database/Seeders/.gitkeep
Normal file
0
Modules/MarketingAddon/Database/Seeders/.gitkeep
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
25
Modules/MarketingAddon/Database/Seeders/OptionSeeder.php
Normal file
25
Modules/MarketingAddon/Database/Seeders/OptionSeeder.php
Normal 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');
|
||||
}
|
||||
}
|
||||
69
Modules/MarketingAddon/Database/Seeders/TemplateSeeder.php
Normal file
69
Modules/MarketingAddon/Database/Seeders/TemplateSeeder.php
Normal 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 </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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
31
Modules/MarketingAddon/composer.json
Normal file
31
Modules/MarketingAddon/composer.json
Normal 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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
0
Modules/MarketingAddon/config/.gitkeep
Normal file
0
Modules/MarketingAddon/config/.gitkeep
Normal file
5
Modules/MarketingAddon/config/config.php
Normal file
5
Modules/MarketingAddon/config/config.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'MarketingAddon',
|
||||
];
|
||||
12
Modules/MarketingAddon/module.json
Normal file
12
Modules/MarketingAddon/module.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "MarketingAddon",
|
||||
"alias": "marketingaddon",
|
||||
"version": "1.2",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\MarketingAddon\\App\\Providers\\MarketingAddonServiceProvider"
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
15
Modules/MarketingAddon/package.json
Normal file
15
Modules/MarketingAddon/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
0
Modules/MarketingAddon/resources/views/.gitkeep
Normal file
0
Modules/MarketingAddon/resources/views/.gitkeep
Normal 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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
380
Modules/MarketingAddon/resources/views/templates/index.blade.php
Normal file
380
Modules/MarketingAddon/resources/views/templates/index.blade.php
Normal 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
|
||||
0
Modules/MarketingAddon/routes/.gitkeep
Normal file
0
Modules/MarketingAddon/routes/.gitkeep
Normal file
1
Modules/MarketingAddon/routes/api.php
Normal file
1
Modules/MarketingAddon/routes/api.php
Normal file
@@ -0,0 +1 @@
|
||||
<?php
|
||||
22
Modules/MarketingAddon/routes/web.php
Normal file
22
Modules/MarketingAddon/routes/web.php
Normal 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');
|
||||
|
||||
});
|
||||
21
Modules/MarketingAddon/vite.config.js
Normal file
21
Modules/MarketingAddon/vite.config.js
Normal 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,
|
||||
}),
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user