Compare commits
3 Commits
develop
...
feature/em
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b9f1753d6 | ||
|
|
fd1f76ea2d | ||
|
|
c4d17e9c22 |
66
app/Console/Commands/TestBusinessApplicationEmail.php
Normal file
66
app/Console/Commands/TestBusinessApplicationEmail.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Business;
|
||||
use App\Services\SellerNotificationService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TestBusinessApplicationEmail extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'test:business-application-email {business_id?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Test the business application email notification';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$businessId = $this->argument('business_id');
|
||||
|
||||
if ($businessId) {
|
||||
$business = Business::find($businessId);
|
||||
if (! $business) {
|
||||
$this->error("Business with ID {$businessId} not found.");
|
||||
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Get the first business with status 'submitted'
|
||||
$business = Business::where('status', 'submitted')->first();
|
||||
|
||||
if (! $business) {
|
||||
// Get any business
|
||||
$business = Business::first();
|
||||
}
|
||||
|
||||
if (! $business) {
|
||||
$this->error('No businesses found in the database.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("Testing business application email for: {$business->name} (ID: {$business->id})");
|
||||
|
||||
// Send the notification
|
||||
$service = app(SellerNotificationService::class);
|
||||
$service->businessApplicationSubmitted($business);
|
||||
|
||||
$this->info('Email notification sent successfully!');
|
||||
$this->info('Check Mailpit at http://localhost:8025 to view the email.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
63
app/Filament/Resources/EmailTemplateResource.php
Normal file
63
app/Filament/Resources/EmailTemplateResource.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\EmailTemplateResource\Pages\CreateEmailTemplate;
|
||||
use App\Filament\Resources\EmailTemplateResource\Pages\EditEmailTemplate;
|
||||
use App\Filament\Resources\EmailTemplateResource\Pages\ListEmailTemplates;
|
||||
use App\Filament\Resources\EmailTemplateResource\Pages\ViewEmailTemplate;
|
||||
use App\Filament\Resources\EmailTemplateResource\Schemas\EmailTemplateForm;
|
||||
use App\Filament\Resources\EmailTemplateResource\Schemas\EmailTemplateInfolist;
|
||||
use App\Filament\Resources\EmailTemplateResource\Tables\EmailTemplatesTable;
|
||||
use App\Models\EmailTemplate;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class EmailTemplateResource extends Resource
|
||||
{
|
||||
protected static ?string $model = EmailTemplate::class;
|
||||
|
||||
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-envelope';
|
||||
|
||||
protected static \UnitEnum|string|null $navigationGroup = 'System';
|
||||
|
||||
protected static ?int $navigationSort = 10;
|
||||
|
||||
protected static ?string $navigationLabel = 'Email Templates';
|
||||
|
||||
protected static ?string $modelLabel = 'Email Template';
|
||||
|
||||
protected static ?string $pluralModelLabel = 'Email Templates';
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
// Count inactive templates
|
||||
return static::getModel()::where('is_active', false)->count() ?: null;
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return EmailTemplateForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return EmailTemplateInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return EmailTemplatesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListEmailTemplates::route('/'),
|
||||
'create' => CreateEmailTemplate::route('/create'),
|
||||
'view' => ViewEmailTemplate::route('/{record}'),
|
||||
'edit' => EditEmailTemplate::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmailTemplateResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateEmailTemplate extends CreateRecord
|
||||
{
|
||||
protected static string $resource = EmailTemplateResource::class;
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmailTemplateResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditEmailTemplate extends EditRecord
|
||||
{
|
||||
protected static string $resource = EmailTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getRedirectUrl(): string
|
||||
{
|
||||
return $this->getResource()::getUrl('index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmailTemplateResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListEmailTemplates extends ListRecords
|
||||
{
|
||||
protected static string $resource = EmailTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EmailTemplateResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewEmailTemplate extends ViewRecord
|
||||
{
|
||||
protected static string $resource = EmailTemplateResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Schemas;
|
||||
|
||||
use App\Models\EmailTemplate;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Placeholder;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class EmailTemplateForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->columns(1)
|
||||
->components([
|
||||
Section::make('Template Details')
|
||||
->schema([
|
||||
TextInput::make('key')
|
||||
->label('Template Key')
|
||||
->required()
|
||||
->unique(ignoreRecord: true)
|
||||
->regex('/^[a-z0-9_-]+$/')
|
||||
->helperText('Lowercase alphanumeric characters, hyphens and underscores only')
|
||||
->disabled(fn ($context) => $context === 'edit')
|
||||
->dehydrated(fn ($context) => $context === 'create')
|
||||
->columnSpanFull(),
|
||||
|
||||
TextInput::make('name')
|
||||
->label('Template Name')
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->columnSpanFull(),
|
||||
|
||||
TextInput::make('subject')
|
||||
->label('Email Subject')
|
||||
->required()
|
||||
->maxLength(255)
|
||||
->columnSpanFull(),
|
||||
|
||||
Textarea::make('description')
|
||||
->label('Description')
|
||||
->helperText('Describe when this template is used')
|
||||
->rows(3)
|
||||
->columnSpanFull(),
|
||||
|
||||
TextInput::make('available_variables')
|
||||
->label('Available Variables')
|
||||
->helperText('Comma-separated list (e.g., verification_url, email, logo_url)')
|
||||
->afterStateHydrated(function (TextInput $component, $state) {
|
||||
if (is_array($state)) {
|
||||
$component->state(implode(', ', $state));
|
||||
}
|
||||
})
|
||||
->dehydrateStateUsing(function ($state) {
|
||||
if (empty($state)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map('trim', explode(',', $state));
|
||||
})
|
||||
->columnSpanFull(),
|
||||
|
||||
Checkbox::make('is_active')
|
||||
->label('Template is Active')
|
||||
->default(true)
|
||||
->inline(false),
|
||||
])
|
||||
->columns(2),
|
||||
|
||||
Section::make('Email Content')
|
||||
->schema([
|
||||
Textarea::make('body_html')
|
||||
->label('HTML Body')
|
||||
->required()
|
||||
->rows(25)
|
||||
->helperText('Use {{ $variable }} syntax for dynamic content')
|
||||
->columnSpanFull()
|
||||
->extraAttributes(['style' => 'font-family: monospace; font-size: 13px;']),
|
||||
|
||||
Textarea::make('body_text')
|
||||
->label('Plain Text Body (Optional)')
|
||||
->rows(15)
|
||||
->helperText('Plain text fallback for email clients that don\'t support HTML')
|
||||
->columnSpanFull()
|
||||
->extraAttributes(['style' => 'font-family: monospace; font-size: 13px;']),
|
||||
]),
|
||||
|
||||
Section::make('Metadata')
|
||||
->schema([
|
||||
Placeholder::make('created_at')
|
||||
->label('Created At')
|
||||
->content(fn (?EmailTemplate $record): string => $record?->created_at?->diffForHumans() ?? '-'),
|
||||
|
||||
Placeholder::make('updated_at')
|
||||
->label('Last Updated')
|
||||
->content(fn (?EmailTemplate $record): string => $record?->updated_at?->diffForHumans() ?? '-'),
|
||||
])
|
||||
->columns(2)
|
||||
->hidden(fn ($context) => $context === 'create'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Schemas;
|
||||
|
||||
use Filament\Infolists\Components\IconEntry;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Infolists\Components\ViewEntry;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class EmailTemplateInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
TextEntry::make('name')
|
||||
->label('Template Name')
|
||||
->columnSpan(1),
|
||||
|
||||
TextEntry::make('key')
|
||||
->label('Template Key')
|
||||
->badge()
|
||||
->copyable()
|
||||
->copyMessage('Key copied!')
|
||||
->copyMessageDuration(1500)
|
||||
->columnSpan(1),
|
||||
|
||||
TextEntry::make('subject')
|
||||
->label('Email Subject')
|
||||
->columnSpan(2),
|
||||
|
||||
TextEntry::make('description')
|
||||
->label('Description')
|
||||
->columnSpan(2)
|
||||
->placeholder('No description provided'),
|
||||
|
||||
TextEntry::make('available_variables')
|
||||
->label('Available Variables')
|
||||
->badge()
|
||||
->separator(',')
|
||||
->columnSpan(2)
|
||||
->placeholder('No variables defined'),
|
||||
|
||||
IconEntry::make('is_active')
|
||||
->label('Status')
|
||||
->boolean()
|
||||
->trueIcon('heroicon-o-check-circle')
|
||||
->falseIcon('heroicon-o-x-circle')
|
||||
->trueColor('success')
|
||||
->falseColor('danger')
|
||||
->columnSpan(1),
|
||||
|
||||
TextEntry::make('created_at')
|
||||
->label('Created')
|
||||
->dateTime()
|
||||
->since()
|
||||
->columnSpan(1),
|
||||
|
||||
TextEntry::make('updated_at')
|
||||
->label('Last Updated')
|
||||
->dateTime()
|
||||
->since()
|
||||
->columnSpan(1),
|
||||
|
||||
ViewEntry::make('preview')
|
||||
->label('HTML Preview')
|
||||
->viewData(fn ($record) => [
|
||||
'html' => $record->body_html,
|
||||
])
|
||||
->view('filament.email-template-preview')
|
||||
->columnSpan(2),
|
||||
|
||||
TextEntry::make('body_html')
|
||||
->label('HTML Source')
|
||||
->formatStateUsing(fn ($state) => new HtmlString('<pre class="text-xs font-mono bg-gray-100 dark:bg-gray-900 p-4 rounded overflow-x-auto whitespace-pre-wrap">'.htmlspecialchars($state).'</pre>'))
|
||||
->columnSpan(2),
|
||||
|
||||
TextEntry::make('body_text')
|
||||
->label('Plain Text Version')
|
||||
->formatStateUsing(fn ($state) => new HtmlString('<pre class="text-xs font-mono bg-gray-100 dark:bg-gray-900 p-4 rounded overflow-x-auto whitespace-pre-wrap">'.htmlspecialchars($state ?: 'No plain text version').'</pre>'))
|
||||
->columnSpan(2)
|
||||
->hidden(fn ($record) => empty($record->body_text)),
|
||||
])
|
||||
->columns(2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\EmailTemplateResource\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class EmailTemplatesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->label('Template Name')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->weight('bold'),
|
||||
|
||||
TextColumn::make('key')
|
||||
->label('Key')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->fontFamily('mono')
|
||||
->size('sm')
|
||||
->copyable()
|
||||
->copyMessage('Key copied!')
|
||||
->copyMessageDuration(1500),
|
||||
|
||||
TextColumn::make('subject')
|
||||
->label('Subject')
|
||||
->searchable()
|
||||
->limit(50)
|
||||
->wrap(),
|
||||
|
||||
IconColumn::make('is_active')
|
||||
->label('Status')
|
||||
->boolean()
|
||||
->trueIcon('heroicon-o-check-circle')
|
||||
->falseIcon('heroicon-o-x-circle')
|
||||
->trueColor('success')
|
||||
->falseColor('danger')
|
||||
->sortable(),
|
||||
|
||||
TextColumn::make('updated_at')
|
||||
->label('Last Updated')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->since()
|
||||
->size('sm'),
|
||||
])
|
||||
->defaultSort('name')
|
||||
->filters([
|
||||
SelectFilter::make('is_active')
|
||||
->label('Status')
|
||||
->options([
|
||||
true => 'Active',
|
||||
false => 'Inactive',
|
||||
]),
|
||||
])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
EditAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
162
app/Mail/Admin/BusinessApplicationSubmittedMail.php
Normal file
162
app/Mail/Admin/BusinessApplicationSubmittedMail.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Admin;
|
||||
|
||||
use App\Models\Business;
|
||||
use App\Models\EmailTemplate;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BusinessApplicationSubmittedMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Business $business
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
$businessType = $this->business->business_type
|
||||
? (Business::BUSINESS_TYPES[$this->business->business_type] ?? $this->business->business_type)
|
||||
: 'Business';
|
||||
|
||||
// Check if custom template exists and use its subject
|
||||
$template = EmailTemplate::getByKey('business_application_submitted');
|
||||
if ($template) {
|
||||
$subject = $template->renderSubject($this->getTemplateVariables());
|
||||
} else {
|
||||
$subject = "New {$businessType} Application - {$this->business->name}";
|
||||
}
|
||||
|
||||
return new Envelope(
|
||||
subject: $subject,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
// Check if custom template exists in database
|
||||
$template = EmailTemplate::getByKey('business_application_submitted');
|
||||
|
||||
if ($template) {
|
||||
// Use custom HTML template from database
|
||||
return new Content(
|
||||
htmlString: $template->render($this->getTemplateVariables()),
|
||||
);
|
||||
}
|
||||
|
||||
// Fall back to blade template
|
||||
return new Content(
|
||||
markdown: 'emails.admin.business-application-submitted',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template variables for rendering
|
||||
*/
|
||||
private function getTemplateVariables(): array
|
||||
{
|
||||
$businessType = $this->business->business_type
|
||||
? (Business::BUSINESS_TYPES[$this->business->business_type] ?? $this->business->business_type)
|
||||
: 'Not specified';
|
||||
|
||||
// Format physical address
|
||||
$physicalAddress = $this->business->physical_address ?? 'Not provided';
|
||||
if ($this->business->physical_city || $this->business->physical_state || $this->business->physical_zipcode) {
|
||||
$physicalAddress .= '<br>'.implode(', ', array_filter([
|
||||
$this->business->physical_city,
|
||||
$this->business->physical_state,
|
||||
$this->business->physical_zipcode,
|
||||
]));
|
||||
}
|
||||
|
||||
// Format billing address
|
||||
$billingAddress = $this->business->billing_address ?? 'Not provided';
|
||||
if ($this->business->billing_city || $this->business->billing_state || $this->business->billing_zipcode) {
|
||||
$billingAddress .= '<br>'.implode(', ', array_filter([
|
||||
$this->business->billing_city,
|
||||
$this->business->billing_state,
|
||||
$this->business->billing_zipcode,
|
||||
]));
|
||||
}
|
||||
|
||||
// Format shipping address
|
||||
$shippingAddress = $this->business->shipping_address ?? 'Not provided';
|
||||
if ($this->business->shipping_city || $this->business->shipping_state || $this->business->shipping_zipcode) {
|
||||
$shippingAddress .= '<br>'.implode(', ', array_filter([
|
||||
$this->business->shipping_city,
|
||||
$this->business->shipping_state,
|
||||
$this->business->shipping_zipcode,
|
||||
]));
|
||||
}
|
||||
|
||||
// Format compliance documents status
|
||||
$documentsStatus = '';
|
||||
$docs = [
|
||||
'w9_form_path' => 'W9 Form',
|
||||
'resale_certificate_path' => 'Resale Certificate',
|
||||
'ato_document_path' => 'ATO Document',
|
||||
'tpt_document_path' => 'TPT Document',
|
||||
'form_5000a_path' => 'Form 5000A',
|
||||
];
|
||||
|
||||
foreach ($docs as $field => $label) {
|
||||
$status = $this->business->$field
|
||||
? '<span class="status-badge status-uploaded">✓ Uploaded</span>'
|
||||
: '<span class="status-badge status-missing">Not uploaded</span>';
|
||||
$documentsStatus .= "<div class=\"info-row\"><span class=\"info-label\">{$label}:</span> {$status}</div>";
|
||||
}
|
||||
|
||||
return [
|
||||
'business_name' => $this->business->name ?? 'N/A',
|
||||
'business_type' => $businessType,
|
||||
'dba_name' => $this->business->dba_name ?? 'N/A',
|
||||
'license_number' => $this->business->license_number ?? 'Not provided',
|
||||
'tin_ein' => $this->business->tin_ein ?? 'Not provided',
|
||||
'submitted_date' => $this->business->application_submitted_at
|
||||
? $this->business->application_submitted_at->format('M j, Y \a\t g:i A')
|
||||
: now()->format('M j, Y \a\t g:i A'),
|
||||
'business_phone' => $this->business->business_phone ?? 'Not provided',
|
||||
'business_email' => $this->business->business_email ?? 'Not provided',
|
||||
'physical_address' => $physicalAddress,
|
||||
'billing_address' => $billingAddress,
|
||||
'shipping_address' => $shippingAddress,
|
||||
'primary_contact_name' => trim(($this->business->primary_contact_first_name ?? '').' '.($this->business->primary_contact_last_name ?? '')) ?: 'Not provided',
|
||||
'primary_contact_email' => $this->business->primary_contact_email ?? 'Not provided',
|
||||
'primary_contact_phone' => $this->business->primary_contact_phone ?? 'Not provided',
|
||||
'ap_contact_name' => trim(($this->business->ap_contact_first_name ?? '').' '.($this->business->ap_contact_last_name ?? '')) ?: 'Not provided',
|
||||
'ap_contact_email' => $this->business->ap_contact_email ?? 'Not provided',
|
||||
'ap_contact_phone' => $this->business->ap_contact_phone ?? 'Not provided',
|
||||
'delivery_contact_name' => trim(($this->business->delivery_contact_first_name ?? '').' '.($this->business->delivery_contact_last_name ?? '')) ?: 'Not provided',
|
||||
'delivery_contact_phone' => $this->business->delivery_contact_phone ?? 'Not provided',
|
||||
'payment_method' => $this->business->payment_method ? ucfirst(str_replace('_', ' ', $this->business->payment_method)) : 'Not specified',
|
||||
'documents_status' => $documentsStatus,
|
||||
'review_url' => route('filament.admin.resources.businesses.edit', $this->business),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
'account_name' => config('app.name'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
69
app/Mail/Concerns/UsesEmailTemplate.php
Normal file
69
app/Mail/Concerns/UsesEmailTemplate.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail\Concerns;
|
||||
|
||||
use App\Models\EmailTemplate;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
|
||||
trait UsesEmailTemplate
|
||||
{
|
||||
/**
|
||||
* Get the template key for this email.
|
||||
* Must be implemented by the class using this trait.
|
||||
*/
|
||||
abstract protected function getTemplateKey(): string;
|
||||
|
||||
/**
|
||||
* Get the fallback view for this email.
|
||||
* Must be implemented by the class using this trait.
|
||||
*/
|
||||
abstract protected function getFallbackView(): string;
|
||||
|
||||
/**
|
||||
* Get the variables for template rendering.
|
||||
* Must be implemented by the class using this trait.
|
||||
*/
|
||||
abstract protected function getTemplateVariables(): array;
|
||||
|
||||
/**
|
||||
* Get the email content, using database template if available.
|
||||
*/
|
||||
protected function getContentWithTemplate(): Content
|
||||
{
|
||||
$template = EmailTemplate::getByKey($this->getTemplateKey());
|
||||
|
||||
if ($template) {
|
||||
// Use custom HTML template from database
|
||||
return new Content(
|
||||
htmlString: $template->render($this->getTemplateVariables()),
|
||||
);
|
||||
}
|
||||
|
||||
// Fall back to blade template
|
||||
$viewType = str_contains($this->getFallbackView(), 'markdown:') ? 'markdown' : 'view';
|
||||
|
||||
if ($viewType === 'markdown') {
|
||||
return new Content(
|
||||
markdown: str_replace('markdown:', '', $this->getFallbackView()),
|
||||
);
|
||||
}
|
||||
|
||||
return new Content(
|
||||
view: $this->getFallbackView(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the email subject, using database template if available.
|
||||
*/
|
||||
protected function getSubjectWithTemplate(string $fallbackSubject): string
|
||||
{
|
||||
$template = EmailTemplate::getByKey($this->getTemplateKey());
|
||||
|
||||
if ($template) {
|
||||
return $template->renderSubject($this->getTemplateVariables());
|
||||
}
|
||||
|
||||
return $fallbackSubject;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Contact;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@@ -13,7 +14,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ExistingContactWelcome extends Mailable implements ShouldQueue
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -29,7 +30,7 @@ class ExistingContactWelcome extends Mailable implements ShouldQueue
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Welcome to Cannabrands - Your Account is Ready!',
|
||||
subject: $this->getSubjectWithTemplate('Welcome to '.config('app.name').' - Your Account is Ready!'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,16 +39,29 @@ class ExistingContactWelcome extends Mailable implements ShouldQueue
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.existing-contact-welcome',
|
||||
with: [
|
||||
'user' => $this->user,
|
||||
'contact' => $this->contact,
|
||||
'company' => $this->contact->company,
|
||||
'loginUrl' => route('login'),
|
||||
'dashboardUrl' => route('buyer.dashboard'),
|
||||
],
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'existing_contact_welcome';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.existing-contact-welcome';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'contact_first_name' => $this->contact->first_name,
|
||||
'company_name' => $this->contact->company->name ?? 'your company',
|
||||
'dashboard_url' => route('buyer.dashboard'),
|
||||
'account_name' => config('app.name'),
|
||||
'support_email' => config('mail.from.address'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Invoices;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class InvoiceReadyMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
public function __construct(
|
||||
public Invoice $invoice
|
||||
@@ -20,15 +21,39 @@ class InvoiceReadyMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Invoice {$this->invoice->invoice_number} Ready for Approval",
|
||||
subject: $this->getSubjectWithTemplate("Invoice {$this->invoice->invoice_number} Ready for Approval"),
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.invoices.invoice-ready',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'invoice_ready';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.invoices.invoice-ready';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'invoice_number' => $this->invoice->invoice_number,
|
||||
'order_number' => $this->invoice->order->order_number ?? 'N/A',
|
||||
'invoice_date' => $this->invoice->invoice_date->format('M j, Y'),
|
||||
'due_date' => $this->invoice->due_date->format('M j, Y'),
|
||||
'amount_due' => number_format($this->invoice->amount_due, 2),
|
||||
'payment_terms' => $this->invoice->order->payment_terms ? ucfirst(str_replace('_', ' ', $this->invoice->order->payment_terms)) : 'N/A',
|
||||
'invoice_url' => route('buyer.invoices.show', $this->invoice),
|
||||
'account_name' => config('app.name'),
|
||||
'support_email' => config('mail.from.address'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Invoices;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PaymentReceivedMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -27,7 +28,7 @@ class PaymentReceivedMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Payment Received for Invoice {$this->invoice->invoice_number}",
|
||||
subject: $this->getSubjectWithTemplate("Payment Received for Invoice {$this->invoice->invoice_number}"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,9 +37,31 @@ class PaymentReceivedMail extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.invoices.payment-received',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'payment_received';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.invoices.payment-received';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'invoice_number' => $this->invoice->invoice_number,
|
||||
'payment_amount' => number_format($this->paymentAmount, 2),
|
||||
'payment_date' => now()->format('M j, Y'),
|
||||
'remaining_balance' => number_format($this->invoice->amount_due, 2),
|
||||
'is_fully_paid' => $this->invoice->isPaid() ? 'Fully Paid' : 'Partial Payment',
|
||||
'invoice_url' => route('buyer.invoices.show', $this->invoice),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Orders;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderAcceptedMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -26,7 +27,7 @@ class OrderAcceptedMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Order {$this->order->order_number} Accepted",
|
||||
subject: $this->getSubjectWithTemplate("Order {$this->order->order_number} Accepted"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,9 +36,39 @@ class OrderAcceptedMail extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.orders.order-accepted',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template key for this email.
|
||||
*/
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'order_accepted';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback view for this email.
|
||||
*/
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.orders.order-accepted';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variables for template rendering.
|
||||
*/
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'order_number' => $this->order->order_number,
|
||||
'order_date' => $this->order->created_at->format('M j, Y'),
|
||||
'total_items' => $this->order->items->count(),
|
||||
'order_total' => number_format($this->order->total, 2),
|
||||
'order_url' => route('buyer.orders.show', $this->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Orders;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderDeliveredMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
public function __construct(
|
||||
public Order $order
|
||||
@@ -20,15 +21,36 @@ class OrderDeliveredMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Order {$this->order->order_number} Delivered",
|
||||
subject: $this->getSubjectWithTemplate("Order {$this->order->order_number} Delivered"),
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.orders.order-delivered',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'order_delivered';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.orders.order-delivered';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'order_number' => $this->order->order_number,
|
||||
'delivery_date' => $this->order->delivered_at ? $this->order->delivered_at->format('M j, Y') : now()->format('M j, Y'),
|
||||
'total_items' => $this->order->items->count(),
|
||||
'order_total' => number_format($this->order->total, 2),
|
||||
'order_url' => route('buyer.orders.show', $this->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Orders;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderReadyForDeliveryMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
public function __construct(
|
||||
public Order $order
|
||||
@@ -20,15 +21,46 @@ class OrderReadyForDeliveryMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Order {$this->order->order_number} Ready for Delivery",
|
||||
subject: $this->getSubjectWithTemplate("Order {$this->order->order_number} Ready for Delivery"),
|
||||
);
|
||||
}
|
||||
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.orders.ready-for-delivery',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'order_ready_for_delivery';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.orders.ready-for-delivery';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
$deliveryAddress = $this->order->company->shipping_address ?? $this->order->company->physical_address ?? 'Not specified';
|
||||
if ($this->order->company->shipping_city || $this->order->company->shipping_state) {
|
||||
$deliveryAddress .= ', '.implode(', ', array_filter([
|
||||
$this->order->company->shipping_city,
|
||||
$this->order->company->shipping_state,
|
||||
$this->order->company->shipping_zipcode,
|
||||
]));
|
||||
}
|
||||
|
||||
return [
|
||||
'order_number' => $this->order->order_number,
|
||||
'order_date' => $this->order->created_at->format('M j, Y'),
|
||||
'total_items' => $this->order->items->count(),
|
||||
'order_total' => number_format($this->order->total, 2),
|
||||
'delivery_address' => $deliveryAddress,
|
||||
'order_url' => route('buyer.orders.show', $this->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
public function attachments(): array
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Seller;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class NewOrderReceivedMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -26,7 +27,7 @@ class NewOrderReceivedMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "New Order Received - {$this->order->order_number}",
|
||||
subject: $this->getSubjectWithTemplate("New Order Received - {$this->order->order_number}"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,9 +36,42 @@ class NewOrderReceivedMail extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.seller.new-order-received',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template key for this email.
|
||||
*/
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'seller_new_order_received';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback view for this email.
|
||||
*/
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.seller.new-order-received';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variables for template rendering.
|
||||
*/
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'order_number' => $this->order->order_number,
|
||||
'customer_name' => $this->order->company->name ?? 'Unknown',
|
||||
'order_date' => $this->order->created_at->format('M j, Y \a\t g:i A'),
|
||||
'total_items' => $this->order->items->count(),
|
||||
'order_total' => number_format($this->order->total, 2),
|
||||
'payment_terms' => $this->order->payment_terms ? ucfirst(str_replace('_', ' ', $this->order->payment_terms)) : 'N/A',
|
||||
'customer_notes' => $this->order->notes ?? 'None',
|
||||
'order_url' => route('filament.admin.resources.orders.view', $this->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Seller;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderCancelledMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -26,7 +27,7 @@ class OrderCancelledMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Order Cancelled - {$this->order->order_number}",
|
||||
subject: $this->getSubjectWithTemplate("Order {$this->order->order_number} Cancelled"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,9 +36,40 @@ class OrderCancelledMail extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.seller.order-cancelled',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template key for this email.
|
||||
*/
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'seller_order_cancelled';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback view for this email.
|
||||
*/
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.seller.order-cancelled';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variables for template rendering.
|
||||
*/
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'order_number' => $this->order->order_number,
|
||||
'customer_name' => $this->order->company->name ?? 'Unknown',
|
||||
'order_date' => $this->order->created_at->format('M j, Y'),
|
||||
'order_total' => number_format($this->order->total, 2),
|
||||
'cancellation_reason' => $this->order->cancellation_reason ?? 'Not specified',
|
||||
'order_url' => route('filament.admin.resources.orders.view', $this->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail\Seller;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -11,7 +12,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PaymentReceivedMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
@@ -27,7 +28,7 @@ class PaymentReceivedMail extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Payment Received - {$this->invoice->invoice_number}",
|
||||
subject: $this->getSubjectWithTemplate("Payment Received for Invoice {$this->invoice->invoice_number}"),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,9 +37,40 @@ class PaymentReceivedMail extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.seller.payment-received',
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template key for this email.
|
||||
*/
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'seller_payment_received';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fallback view for this email.
|
||||
*/
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'markdown:emails.seller.payment-received';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variables for template rendering.
|
||||
*/
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'invoice_number' => $this->invoice->invoice_number,
|
||||
'payment_amount' => number_format($this->paymentAmount, 2),
|
||||
'remaining_balance' => number_format($this->invoice->amount_due, 2),
|
||||
'is_fully_paid' => $this->invoice->isPaid() ? 'Fully Paid' : 'Partial Payment',
|
||||
'order_number' => $this->invoice->order->order_number ?? 'N/A',
|
||||
'order_url' => route('filament.admin.resources.orders.view', $this->invoice->order),
|
||||
'account_name' => config('app.name'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Mail\Concerns\UsesEmailTemplate;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
@@ -13,7 +14,7 @@ use Illuminate\Support\Facades\URL;
|
||||
|
||||
class UserApproved extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable, SerializesModels, UsesEmailTemplate;
|
||||
|
||||
public User $user;
|
||||
|
||||
@@ -40,7 +41,7 @@ class UserApproved extends Mailable
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Welcome to Cannabrands - Your Account is Approved!',
|
||||
subject: $this->getSubjectWithTemplate('Welcome to '.config('app.name').' - Your Account is Approved!'),
|
||||
from: config('mail.from.address', 'hello@cannabrands.com'),
|
||||
);
|
||||
}
|
||||
@@ -50,13 +51,28 @@ class UserApproved extends Mailable
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'emails.user-approved',
|
||||
with: [
|
||||
'user' => $this->user,
|
||||
'loginUrl' => $this->loginUrl,
|
||||
]
|
||||
);
|
||||
return $this->getContentWithTemplate();
|
||||
}
|
||||
|
||||
protected function getTemplateKey(): string
|
||||
{
|
||||
return 'user_approved';
|
||||
}
|
||||
|
||||
protected function getFallbackView(): string
|
||||
{
|
||||
return 'emails.user-approved';
|
||||
}
|
||||
|
||||
protected function getTemplateVariables(): array
|
||||
{
|
||||
return [
|
||||
'user_name' => $this->user->name,
|
||||
'login_url' => $this->loginUrl,
|
||||
'account_name' => config('app.name'),
|
||||
'support_email' => config('mail.from.address'),
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
67
app/Models/EmailTemplate.php
Normal file
67
app/Models/EmailTemplate.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class EmailTemplate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'name',
|
||||
'subject',
|
||||
'body_html',
|
||||
'body_text',
|
||||
'available_variables',
|
||||
'description',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'available_variables' => 'array',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get a template by its key
|
||||
*/
|
||||
public static function getByKey(string $key): ?self
|
||||
{
|
||||
return self::where('key', $key)
|
||||
->where('is_active', true)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the template with provided variables
|
||||
*/
|
||||
public function render(array $variables = []): string
|
||||
{
|
||||
$html = $this->body_html;
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
$html = str_replace('{{ $'.$key.' }}', $value, $html);
|
||||
$html = str_replace('{{$'.$key.'}}', $value, $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the subject with provided variables
|
||||
*/
|
||||
public function renderSubject(array $variables = []): string
|
||||
{
|
||||
$subject = $this->subject;
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
$subject = str_replace('{{ $'.$key.' }}', $value, $subject);
|
||||
$subject = str_replace('{{$'.$key.'}}', $value, $subject);
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\EmailTemplate;
|
||||
use App\Models\EmailVerification;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
@@ -72,9 +73,29 @@ class EmailVerificationService
|
||||
*/
|
||||
public function sendVerificationEmail(string $email, string $token): void
|
||||
{
|
||||
$verificationUrl = route('buyer.register.verify', ['token' => $token]);
|
||||
$verificationUrl = route('buyer.register.complete', ['token' => $token]);
|
||||
|
||||
Mail::to($email)->send(new \App\Mail\RegistrationVerificationMail($verificationUrl, $email));
|
||||
// Try to use database template first, fall back to mailable if not found
|
||||
$template = EmailTemplate::getByKey('registration_verification');
|
||||
|
||||
if ($template) {
|
||||
$variables = [
|
||||
'verification_url' => $verificationUrl,
|
||||
'email' => $email,
|
||||
'logo_url' => asset('assets/images/canna_white.png'),
|
||||
'account_name' => 'Cannabrands',
|
||||
'support_email' => 'hello@cannabrands.com',
|
||||
];
|
||||
|
||||
Mail::send([], [], function ($message) use ($template, $email, $variables) {
|
||||
$message->to($email)
|
||||
->subject($template->renderSubject($variables))
|
||||
->html($template->render($variables));
|
||||
});
|
||||
} else {
|
||||
// Fallback to original mailable
|
||||
Mail::to($email)->send(new \App\Mail\RegistrationVerificationMail($verificationUrl, $email));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Mail\Admin\BusinessApplicationSubmittedMail;
|
||||
use App\Mail\Seller\NewOrderReceivedMail;
|
||||
use App\Mail\Seller\OrderCancelledMail;
|
||||
use App\Mail\Seller\PaymentReceivedMail as SellerPaymentReceivedMail;
|
||||
@@ -113,6 +114,9 @@ class SellerNotificationService
|
||||
? (\App\Models\Business::BUSINESS_TYPES[$business->business_type] ?? $business->business_type)
|
||||
: 'Unknown';
|
||||
|
||||
// Send email notification
|
||||
Mail::to($admin->email)->send(new BusinessApplicationSubmittedMail($business));
|
||||
|
||||
// Create in-app notification
|
||||
$this->notificationService->create(
|
||||
user: $admin,
|
||||
|
||||
@@ -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('email_templates', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique()->comment('Unique identifier for the template');
|
||||
$table->string('name')->comment('Human-readable name');
|
||||
$table->string('subject');
|
||||
$table->text('body_html')->comment('HTML version of email');
|
||||
$table->text('body_text')->nullable()->comment('Plain text version');
|
||||
$table->json('available_variables')->nullable()->comment('List of variables that can be used in this template');
|
||||
$table->text('description')->nullable()->comment('Description of when this template is used');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('email_templates');
|
||||
}
|
||||
};
|
||||
@@ -16,6 +16,8 @@ class DatabaseSeeder extends Seeder
|
||||
$this->call(RoleSeeder::class);
|
||||
// Always seed super admin after roles exist
|
||||
$this->call(SuperAdminSeeder::class);
|
||||
// Always seed email templates
|
||||
$this->call(EmailTemplateSeeder::class);
|
||||
// Only seed test/demo users in local or staging
|
||||
if (app()->environment(['local', 'staging'])) {
|
||||
$this->call(DevSeeder::class);
|
||||
|
||||
901
database/seeders/EmailTemplateSeeder.php
Normal file
901
database/seeders/EmailTemplateSeeder.php
Normal file
@@ -0,0 +1,901 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\EmailTemplate;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class EmailTemplateSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// Registration Verification Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'registration_verification'],
|
||||
[
|
||||
'name' => 'Registration Verification',
|
||||
'subject' => 'Complete Your Cannabrands Registration',
|
||||
'body_html' => $this->getRegistrationVerificationHtml(),
|
||||
'body_text' => $this->getRegistrationVerificationText(),
|
||||
'available_variables' => [
|
||||
'verification_url',
|
||||
'email',
|
||||
'logo_url',
|
||||
'account_name',
|
||||
'support_email',
|
||||
],
|
||||
'description' => 'Sent to users when they start the registration process to verify their email address.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Business Application Submitted Email (Admin Notification)
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'business_application_submitted'],
|
||||
[
|
||||
'name' => 'Business Application Submitted (Admin)',
|
||||
'subject' => 'New {{ $business_type }} Application - {{ $business_name }}',
|
||||
'body_html' => $this->getBusinessApplicationSubmittedHtml(),
|
||||
'body_text' => $this->getBusinessApplicationSubmittedText(),
|
||||
'available_variables' => [
|
||||
'business_name', 'business_type', 'dba_name', 'license_number', 'tin_ein',
|
||||
'submitted_date', 'business_phone', 'business_email', 'physical_address',
|
||||
'billing_address', 'shipping_address', 'primary_contact_name',
|
||||
'primary_contact_email', 'primary_contact_phone', 'ap_contact_name',
|
||||
'ap_contact_email', 'ap_contact_phone', 'delivery_contact_name',
|
||||
'delivery_contact_phone', 'payment_method', 'documents_status',
|
||||
'review_url', 'logo_url', 'account_name',
|
||||
],
|
||||
'description' => 'Sent to super admins when a buyer or seller submits their business application for approval.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// User Account Approved Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'user_approved'],
|
||||
[
|
||||
'name' => 'User Account Approved',
|
||||
'subject' => 'Welcome to {{ $account_name }} - Your Account is Approved!',
|
||||
'body_html' => $this->getUserApprovedHtml(),
|
||||
'body_text' => $this->getUserApprovedText(),
|
||||
'available_variables' => ['user_name', 'login_url', 'account_name', 'support_email', 'logo_url'],
|
||||
'description' => 'Sent to users when their account is approved by admins.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Existing Contact Welcome Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'existing_contact_welcome'],
|
||||
[
|
||||
'name' => 'Existing Contact Welcome',
|
||||
'subject' => 'Welcome to {{ $account_name }} - Your Account is Ready!',
|
||||
'body_html' => $this->getExistingContactWelcomeHtml(),
|
||||
'body_text' => $this->getExistingContactWelcomeText(),
|
||||
'available_variables' => ['contact_first_name', 'company_name', 'dashboard_url', 'account_name', 'support_email', 'logo_url'],
|
||||
'description' => 'Sent to existing contacts when their user account is created.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Order Accepted Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'order_accepted'],
|
||||
[
|
||||
'name' => 'Order Accepted',
|
||||
'subject' => 'Order {{ $order_number }} Accepted',
|
||||
'body_html' => $this->getOrderAcceptedHtml(),
|
||||
'body_text' => $this->getOrderAcceptedText(),
|
||||
'available_variables' => ['order_number', 'order_date', 'total_items', 'order_total', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to buyers when their order is accepted.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Order Ready for Delivery Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'order_ready_for_delivery'],
|
||||
[
|
||||
'name' => 'Order Ready for Delivery',
|
||||
'subject' => 'Order {{ $order_number }} Ready for Delivery',
|
||||
'body_html' => $this->getOrderReadyForDeliveryHtml(),
|
||||
'body_text' => $this->getOrderReadyForDeliveryText(),
|
||||
'available_variables' => ['order_number', 'order_date', 'total_items', 'order_total', 'delivery_address', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to buyers when their order is ready for delivery.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Order Delivered Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'order_delivered'],
|
||||
[
|
||||
'name' => 'Order Delivered',
|
||||
'subject' => 'Order {{ $order_number }} Delivered',
|
||||
'body_html' => $this->getOrderDeliveredHtml(),
|
||||
'body_text' => $this->getOrderDeliveredText(),
|
||||
'available_variables' => ['order_number', 'delivery_date', 'total_items', 'order_total', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to buyers when their order is delivered.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// New Order Received (Seller)
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'seller_new_order_received'],
|
||||
[
|
||||
'name' => 'New Order Received (Seller)',
|
||||
'subject' => 'New Order Received - {{ $order_number }}',
|
||||
'body_html' => $this->getSellerNewOrderReceivedHtml(),
|
||||
'body_text' => $this->getSellerNewOrderReceivedText(),
|
||||
'available_variables' => ['order_number', 'customer_name', 'order_date', 'total_items', 'order_total', 'payment_terms', 'customer_notes', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to sellers/admins when a new order is received.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Order Cancelled (Seller)
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'seller_order_cancelled'],
|
||||
[
|
||||
'name' => 'Order Cancelled (Seller)',
|
||||
'subject' => 'Order {{ $order_number }} Cancelled',
|
||||
'body_html' => $this->getSellerOrderCancelledHtml(),
|
||||
'body_text' => $this->getSellerOrderCancelledText(),
|
||||
'available_variables' => ['order_number', 'customer_name', 'order_date', 'order_total', 'cancellation_reason', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to sellers/admins when an order is cancelled.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Payment Received (Seller)
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'seller_payment_received'],
|
||||
[
|
||||
'name' => 'Payment Received (Seller)',
|
||||
'subject' => 'Payment Received for Invoice {{ $invoice_number }}',
|
||||
'body_html' => $this->getSellerPaymentReceivedHtml(),
|
||||
'body_text' => $this->getSellerPaymentReceivedText(),
|
||||
'available_variables' => ['invoice_number', 'payment_amount', 'remaining_balance', 'is_fully_paid', 'order_number', 'order_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to sellers/admins when payment is received for an invoice.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Invoice Ready Email
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'invoice_ready'],
|
||||
[
|
||||
'name' => 'Invoice Ready',
|
||||
'subject' => 'Invoice {{ $invoice_number }} Ready for Approval',
|
||||
'body_html' => $this->getInvoiceReadyHtml(),
|
||||
'body_text' => $this->getInvoiceReadyText(),
|
||||
'available_variables' => ['invoice_number', 'order_number', 'invoice_date', 'due_date', 'amount_due', 'payment_terms', 'invoice_url', 'account_name', 'support_email', 'logo_url'],
|
||||
'description' => 'Sent to buyers when an invoice is ready for approval.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Payment Received (Buyer)
|
||||
EmailTemplate::updateOrCreate(
|
||||
['key' => 'payment_received'],
|
||||
[
|
||||
'name' => 'Payment Received (Buyer)',
|
||||
'subject' => 'Payment Received for Invoice {{ $invoice_number }}',
|
||||
'body_html' => $this->getPaymentReceivedHtml(),
|
||||
'body_text' => $this->getPaymentReceivedText(),
|
||||
'available_variables' => ['invoice_number', 'payment_amount', 'payment_date', 'remaining_balance', 'is_fully_paid', 'invoice_url', 'account_name', 'logo_url'],
|
||||
'description' => 'Sent to buyers when their payment is received.',
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$this->command->info('Email templates seeded successfully!');
|
||||
}
|
||||
|
||||
private function getRegistrationVerificationHtml(): string
|
||||
{
|
||||
return <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #f4f5f7;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #172B4D;
|
||||
}
|
||||
.main {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #014847 0%, #014342 100%);
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.header img {
|
||||
height: 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
.content {
|
||||
padding: 30px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.content p {
|
||||
margin: 20px 0 10px;
|
||||
}
|
||||
.highlight-box {
|
||||
background: #f1f5f9;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.button-container {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #015b59;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
padding: 14px 28px;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #014847;
|
||||
}
|
||||
.footer {
|
||||
padding: 30px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #6b7280;
|
||||
}
|
||||
.footer a {
|
||||
color: #015b59;
|
||||
text-decoration: none;
|
||||
}
|
||||
.expiry-note {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<img src="{{ $logo_url }}" alt="{{ $account_name }} Logo" />
|
||||
<h1>Complete Your Registration</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Hello,</p>
|
||||
|
||||
<p>Thank you for starting your registration with {{ $account_name }}! To complete your account setup, please click the button below to verify your email address and fill out your profile.</p>
|
||||
|
||||
<div class="highlight-box">
|
||||
<strong>Email:</strong> {{ $email }}
|
||||
</div>
|
||||
|
||||
<div class="button-container">
|
||||
<a href="{{ $verification_url }}" class="button">Complete Registration</a>
|
||||
</div>
|
||||
|
||||
<p class="expiry-note">
|
||||
⏱️ This link will expire in 24 hours for security reasons.
|
||||
</p>
|
||||
|
||||
<p>If you didn't request this registration, you can safely ignore this email.</p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>Need help?</p>
|
||||
<p>Contact us at <a href="mailto:{{ $support_email }}">{{ $support_email }}</a>.</p>
|
||||
<p>– The {{ $account_name }} Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
private function getRegistrationVerificationText(): string
|
||||
{
|
||||
return <<<'TEXT'
|
||||
Complete Your Registration
|
||||
|
||||
Hello,
|
||||
|
||||
Thank you for starting your registration with {{ $account_name }}! To complete your account setup, please visit the link below to verify your email address and fill out your profile.
|
||||
|
||||
Email: {{ $email }}
|
||||
|
||||
Complete your registration here:
|
||||
{{ $verification_url }}
|
||||
|
||||
⏱️ This link will expire in 24 hours for security reasons.
|
||||
|
||||
If you didn't request this registration, you can safely ignore this email.
|
||||
|
||||
Need help?
|
||||
Contact us at {{ $support_email }}.
|
||||
|
||||
– The {{ $account_name }} Team
|
||||
TEXT;
|
||||
}
|
||||
|
||||
private function getBusinessApplicationSubmittedHtml(): string
|
||||
{
|
||||
return <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #f4f5f7;
|
||||
font-family: Arial, sans-serif;
|
||||
color: #172B4D;
|
||||
}
|
||||
.main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
background: linear-gradient(135deg, #014847 0%, #014342 100%);
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
.header img {
|
||||
height: 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-weight: normal;
|
||||
}
|
||||
.content {
|
||||
padding: 30px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.content p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.info-section {
|
||||
background: #f9fafb;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid #015b59;
|
||||
}
|
||||
.info-section h2 {
|
||||
font-size: 18px;
|
||||
margin: 0 0 15px 0;
|
||||
color: #014847;
|
||||
}
|
||||
.info-row {
|
||||
margin: 8px 0;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
color: #374151;
|
||||
}
|
||||
.info-value {
|
||||
color: #6b7280;
|
||||
}
|
||||
.button-container {
|
||||
text-align: center;
|
||||
margin: 30px 0;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
background-color: #015b59;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
padding: 14px 28px;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.button:hover {
|
||||
background-color: #014847;
|
||||
}
|
||||
.footer {
|
||||
padding: 30px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: #6b7280;
|
||||
background: #f9fafb;
|
||||
}
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.status-uploaded {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
.status-missing {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<img src="{{ $logo_url }}" alt="{{ $account_name }} Logo" />
|
||||
<h1>New Business Application</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>A new <strong>{{ $business_type }}</strong> has submitted their application and is ready for review.</p>
|
||||
|
||||
<div class="info-section">
|
||||
<h2>Business Information</h2>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Business Name:</span>
|
||||
<span class="info-value">{{ $business_name }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Business Type:</span>
|
||||
<span class="info-value">{{ $business_type }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">License Number:</span>
|
||||
<span class="info-value">{{ $license_number }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">TIN/EIN:</span>
|
||||
<span class="info-value">{{ $tin_ein }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Submitted:</span>
|
||||
<span class="info-value">{{ $submitted_date }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<h2>Contact Information</h2>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Phone:</span>
|
||||
<span class="info-value">{{ $business_phone }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Email:</span>
|
||||
<span class="info-value">{{ $business_email }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Physical Address:</span>
|
||||
<span class="info-value">{{ $physical_address }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<h2>Primary Contact</h2>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Name:</span>
|
||||
<span class="info-value">{{ $primary_contact_name }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Email:</span>
|
||||
<span class="info-value">{{ $primary_contact_email }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Phone:</span>
|
||||
<span class="info-value">{{ $primary_contact_phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<h2>Compliance Documents</h2>
|
||||
{{ $documents_status }}
|
||||
</div>
|
||||
|
||||
<div class="button-container">
|
||||
<a href="{{ $review_url }}" class="button">Review & Approve Application</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>This application requires your review and approval before the business can begin using the platform.</p>
|
||||
<p>– The {{ $account_name }} Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
private function getBusinessApplicationSubmittedText(): string
|
||||
{
|
||||
return <<<'TEXT'
|
||||
New Business Application Submitted
|
||||
|
||||
A new {{ $business_type }} has submitted their application and is ready for review.
|
||||
|
||||
BUSINESS INFORMATION
|
||||
--------------------
|
||||
Business Name: {{ $business_name }}
|
||||
Business Type: {{ $business_type }}
|
||||
License Number: {{ $license_number }}
|
||||
TIN/EIN: {{ $tin_ein }}
|
||||
Submitted: {{ $submitted_date }}
|
||||
|
||||
CONTACT INFORMATION
|
||||
-------------------
|
||||
Phone: {{ $business_phone }}
|
||||
Email: {{ $business_email }}
|
||||
Physical Address: {{ $physical_address }}
|
||||
|
||||
PRIMARY CONTACT
|
||||
---------------
|
||||
Name: {{ $primary_contact_name }}
|
||||
Email: {{ $primary_contact_email }}
|
||||
Phone: {{ $primary_contact_phone }}
|
||||
|
||||
COMPLIANCE DOCUMENTS
|
||||
--------------------
|
||||
{{ $documents_status }}
|
||||
|
||||
Review and approve this application here:
|
||||
{{ $review_url }}
|
||||
|
||||
This application requires your review and approval before the business can begin using the platform.
|
||||
|
||||
– The {{ $account_name }} Team
|
||||
TEXT;
|
||||
}
|
||||
|
||||
private function getUserApprovedHtml(): string
|
||||
{
|
||||
return <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { margin: 0; background-color: #f4f5f7; font-family: Arial, sans-serif; color: #172B4D; }
|
||||
.main { max-width: 600px; margin: auto; background: #ffffff; border-radius: 8px; overflow: hidden; }
|
||||
.header { background: linear-gradient(135deg, #014847 0%, #014342 100%); padding: 30px; text-align: center; }
|
||||
.header img { height: 32px; margin-bottom: 20px; }
|
||||
.header h1 { font-size: 24px; margin: 0; color: #fff; font-weight: normal; }
|
||||
.content { padding: 30px; font-size: 16px; }
|
||||
.button { display: inline-block; background-color: #015b59; color: #ffffff; text-decoration: none; padding: 14px 28px; font-weight: bold; border-radius: 5px; }
|
||||
.footer { padding: 30px; font-size: 14px; color: #6b7280; background: #f9fafb; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<img src="{{ $logo_url }}" alt="{{ $account_name }} Logo" />
|
||||
<h1>Account Approved!</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Hi {{ $user_name }},</p>
|
||||
<p>Great news! Your {{ $account_name }} account has been approved. You can now access the platform.</p>
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ $login_url }}" class="button">Access Your Dashboard</a>
|
||||
</div>
|
||||
<p><small>This link expires in 48 hours for security.</small></p>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>Need help? Contact us at {{ $support_email }}</p>
|
||||
<p>– The {{ $account_name }} Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
private function getUserApprovedText(): string
|
||||
{
|
||||
return <<<'TEXT'
|
||||
Account Approved!
|
||||
|
||||
Hi {{ $user_name }},
|
||||
|
||||
Great news! Your {{ $account_name }} account has been approved. You can now access the platform.
|
||||
|
||||
Access your dashboard here:
|
||||
{{ $login_url }}
|
||||
|
||||
This link expires in 48 hours for security.
|
||||
|
||||
Need help? Contact us at {{ $support_email }}
|
||||
|
||||
– The {{ $account_name }} Team
|
||||
TEXT;
|
||||
}
|
||||
|
||||
private function getExistingContactWelcomeHtml(): string
|
||||
{
|
||||
return <<<'HTML'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { margin: 0; background-color: #f4f5f7; font-family: Arial, sans-serif; color: #172B4D; }
|
||||
.main { max-width: 600px; margin: auto; background: #ffffff; border-radius: 8px; overflow: hidden; }
|
||||
.header { background: linear-gradient(135deg, #014847 0%, #014342 100%); padding: 30px; text-align: center; }
|
||||
.header img { height: 32px; margin-bottom: 20px; }
|
||||
.header h1 { font-size: 24px; margin: 0; color: #fff; font-weight: normal; }
|
||||
.content { padding: 30px; font-size: 16px; }
|
||||
.button { display: inline-block; background-color: #015b59; color: #ffffff; text-decoration: none; padding: 14px 28px; font-weight: bold; border-radius: 5px; }
|
||||
.footer { padding: 30px; font-size: 14px; color: #6b7280; background: #f9fafb; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<img src="{{ $logo_url }}" alt="{{ $account_name }} Logo" />
|
||||
<h1>Welcome Back, {{ $contact_first_name }}!</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Your account has been created and linked to {{ $company_name }}.</p>
|
||||
<p>You can now browse products, place orders, and manage your business profile.</p>
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ $dashboard_url }}" class="button">Go to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>Need help? Contact us at {{ $support_email }}</p>
|
||||
<p>– The {{ $account_name }} Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
private function getExistingContactWelcomeText(): string
|
||||
{
|
||||
return <<<'TEXT'
|
||||
Welcome Back, {{ $contact_first_name }}!
|
||||
|
||||
Your account has been created and linked to {{ $company_name }}.
|
||||
|
||||
You can now browse products, place orders, and manage your business profile.
|
||||
|
||||
Go to your dashboard:
|
||||
{{ $dashboard_url }}
|
||||
|
||||
Need help? Contact us at {{ $support_email }}
|
||||
|
||||
– The {{ $account_name }} Team
|
||||
TEXT;
|
||||
}
|
||||
|
||||
private function getOrderAcceptedHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Order Accepted',
|
||||
'Good news! Your order <strong>{{ $order_number }}</strong> has been accepted.',
|
||||
'Our team is now preparing your order. You\'ll receive another email when it\'s ready for delivery.'
|
||||
);
|
||||
}
|
||||
|
||||
private function getOrderAcceptedText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Order Accepted',
|
||||
'Good news! Your order {{ $order_number }} has been accepted.',
|
||||
'Our team is now preparing your order. You\'ll receive another email when it\'s ready for delivery.'
|
||||
);
|
||||
}
|
||||
|
||||
private function getOrderReadyForDeliveryHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Order Ready for Delivery',
|
||||
'Your order <strong>{{ $order_number }}</strong> is ready for delivery!',
|
||||
'Delivery Address: {{ $delivery_address }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getOrderReadyForDeliveryText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Order Ready for Delivery',
|
||||
'Your order {{ $order_number }} is ready for delivery!',
|
||||
'Delivery Address: {{ $delivery_address }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getOrderDeliveredHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Order Delivered',
|
||||
'Your order <strong>{{ $order_number }}</strong> has been delivered!',
|
||||
'Delivery Date: {{ $delivery_date }}<br>Thank you for your business!'
|
||||
);
|
||||
}
|
||||
|
||||
private function getOrderDeliveredText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Order Delivered',
|
||||
'Your order {{ $order_number }} has been delivered!',
|
||||
'Delivery Date: {{ $delivery_date }}. Thank you for your business!'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerNewOrderReceivedHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'New Order Received',
|
||||
'You have received a new order <strong>{{ $order_number }}</strong> from {{ $customer_name }}.',
|
||||
'Payment Terms: {{ $payment_terms }}<br>Customer Notes: {{ $customer_notes }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerNewOrderReceivedText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'New Order Received',
|
||||
'You have received a new order {{ $order_number }} from {{ $customer_name }}.',
|
||||
'Payment Terms: {{ $payment_terms }}. Customer Notes: {{ $customer_notes }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerOrderCancelledHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Order Cancelled',
|
||||
'Order <strong>{{ $order_number }}</strong> from {{ $customer_name }} has been cancelled.',
|
||||
'Cancellation Reason: {{ $cancellation_reason }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerOrderCancelledText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Order Cancelled',
|
||||
'Order {{ $order_number }} from {{ $customer_name }} has been cancelled.',
|
||||
'Cancellation Reason: {{ $cancellation_reason }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerPaymentReceivedHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Payment Received',
|
||||
'Payment of <strong>${{ $payment_amount }}</strong> received for invoice {{ $invoice_number }}.',
|
||||
'Remaining Balance: ${{ $remaining_balance }}<br>Status: {{ $is_fully_paid }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSellerPaymentReceivedText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Payment Received',
|
||||
'Payment of ${{ $payment_amount }} received for invoice {{ $invoice_number }}.',
|
||||
'Remaining Balance: ${{ $remaining_balance }}. Status: {{ $is_fully_paid }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getInvoiceReadyHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Invoice Ready',
|
||||
'Your invoice <strong>{{ $invoice_number }}</strong> for order {{ $order_number }} is ready.',
|
||||
'Amount Due: ${{ $amount_due }}<br>Due Date: {{ $due_date }}<br>Payment Terms: {{ $payment_terms }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getInvoiceReadyText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Invoice Ready',
|
||||
'Your invoice {{ $invoice_number }} for order {{ $order_number }} is ready.',
|
||||
'Amount Due: ${{ $amount_due }}. Due Date: {{ $due_date }}. Payment Terms: {{ $payment_terms }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getPaymentReceivedHtml(): string
|
||||
{
|
||||
return $this->getSimpleOrderTemplate(
|
||||
'Payment Received',
|
||||
'Your payment of <strong>${{ $payment_amount }}</strong> for invoice {{ $invoice_number }} has been received.',
|
||||
'Payment Date: {{ $payment_date }}<br>Remaining Balance: ${{ $remaining_balance }}<br>Status: {{ $is_fully_paid }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getPaymentReceivedText(): string
|
||||
{
|
||||
return $this->getSimpleOrderTextTemplate(
|
||||
'Payment Received',
|
||||
'Your payment of ${{ $payment_amount }} for invoice {{ $invoice_number }} has been received.',
|
||||
'Payment Date: {{ $payment_date }}. Remaining Balance: ${{ $remaining_balance }}. Status: {{ $is_fully_paid }}'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSimpleOrderTemplate(string $title, string $mainMessage, string $details): string
|
||||
{
|
||||
return <<<HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { margin: 0; background-color: #f4f5f7; font-family: Arial, sans-serif; color: #172B4D; }
|
||||
.main { max-width: 600px; margin: auto; background: #ffffff; border-radius: 8px; overflow: hidden; }
|
||||
.header { background: linear-gradient(135deg, #014847 0%, #014342 100%); padding: 30px; text-align: center; }
|
||||
.header img { height: 32px; margin-bottom: 20px; }
|
||||
.header h1 { font-size: 24px; margin: 0; color: #fff; font-weight: normal; }
|
||||
.content { padding: 30px; font-size: 16px; }
|
||||
.info-box { background: #f9fafb; padding: 20px; border-radius: 6px; margin: 20px 0; }
|
||||
.button { display: inline-block; background-color: #015b59; color: #ffffff; text-decoration: none; padding: 14px 28px; font-weight: bold; border-radius: 5px; }
|
||||
.footer { padding: 30px; font-size: 14px; color: #6b7280; background: #f9fafb; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="header">
|
||||
<img src="{{ \$logo_url }}" alt="{{ \$account_name }} Logo" />
|
||||
<h1>{$title}</h1>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>{$mainMessage}</p>
|
||||
<div class="info-box">
|
||||
<p><strong>Order Number:</strong> {{ \$order_number }}</p>
|
||||
<p><strong>Order Date:</strong> {{ \$order_date }}</p>
|
||||
<p><strong>Total Items:</strong> {{ \$total_items }}</p>
|
||||
<p><strong>Order Total:</strong> \${{ \$order_total }}</p>
|
||||
</div>
|
||||
<p>{$details}</p>
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ \$order_url }}" class="button">View Order Details</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<p>– The {{ \$account_name }} Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
}
|
||||
|
||||
private function getSimpleOrderTextTemplate(string $title, string $mainMessage, string $details): string
|
||||
{
|
||||
return <<<TEXT
|
||||
{$title}
|
||||
|
||||
{$mainMessage}
|
||||
|
||||
Order Number: {{ \$order_number }}
|
||||
Order Date: {{ \$order_date }}
|
||||
Total Items: {{ \$total_items }}
|
||||
Order Total: \${{ \$order_total }}
|
||||
|
||||
{$details}
|
||||
|
||||
View order details:
|
||||
{{ \$order_url }}
|
||||
|
||||
– The {{ \$account_name }} Team
|
||||
TEXT;
|
||||
}
|
||||
}
|
||||
@@ -72,11 +72,11 @@
|
||||
<!-- Registration Form -->
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ route('buyer.register.complete') }}"
|
||||
action="{{ route('buyer.register.complete.process') }}"
|
||||
class="space-y-6"
|
||||
novalidate
|
||||
x-data="{
|
||||
form: $form('post', '{{ route('buyer.register.complete') }}', {
|
||||
form: $form('post', '{{ route('buyer.register.complete.process') }}', {
|
||||
token: '{{ $token }}',
|
||||
first_name: '{{ old('first_name', $prefill['first_name'] ?? '') }}',
|
||||
last_name: '{{ old('last_name', $prefill['last_name'] ?? '') }}',
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
@php
|
||||
$businessTypeLabel = $business->business_type
|
||||
? (\App\Models\Business::BUSINESS_TYPES[$business->business_type] ?? $business->business_type)
|
||||
: 'Not specified';
|
||||
@endphp
|
||||
|
||||
@component('mail::message')
|
||||
# New Business Application Submitted
|
||||
|
||||
A new {{ strtolower($businessTypeLabel) }} application has been submitted and is ready for review.
|
||||
|
||||
## Business Information
|
||||
|
||||
**Business Name:** {{ $business->name }}
|
||||
@if($business->dba_name)
|
||||
**DBA Name:** {{ $business->dba_name }}
|
||||
@endif
|
||||
|
||||
**Business Type:** {{ $businessTypeLabel }}
|
||||
|
||||
**License Number:** {{ $business->license_number ?? 'Not provided' }}
|
||||
|
||||
**TIN/EIN:** {{ $business->tin_ein ?? 'Not provided' }}
|
||||
|
||||
**Submitted:** {{ $business->application_submitted_at ? $business->application_submitted_at->format('M j, Y \\a\\t g:i A') : now()->format('M j, Y \\a\\t g:i A') }}
|
||||
|
||||
## Contact Information
|
||||
|
||||
**Business Phone:** {{ $business->business_phone ?? 'Not provided' }}
|
||||
|
||||
**Business Email:** {{ $business->business_email ?? 'Not provided' }}
|
||||
|
||||
## Location
|
||||
|
||||
**Physical Address:**
|
||||
{{ $business->physical_address ?? 'Not provided' }}
|
||||
@if($business->physical_city || $business->physical_state || $business->physical_zipcode)
|
||||
{{ $business->physical_city }}, {{ $business->physical_state }} {{ $business->physical_zipcode }}
|
||||
@endif
|
||||
|
||||
@if($business->billing_address)
|
||||
**Billing Address:**
|
||||
{{ $business->billing_address }}
|
||||
@if($business->billing_city || $business->billing_state || $business->billing_zipcode)
|
||||
{{ $business->billing_city }}, {{ $business->billing_state }} {{ $business->billing_zipcode }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if($business->shipping_address)
|
||||
**Shipping Address:**
|
||||
{{ $business->shipping_address }}
|
||||
@if($business->shipping_city || $business->shipping_state || $business->shipping_zipcode)
|
||||
{{ $business->shipping_city }}, {{ $business->shipping_state }} {{ $business->shipping_zipcode }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
## Primary Contact
|
||||
|
||||
@if($business->primary_contact_first_name || $business->primary_contact_last_name)
|
||||
**Name:** {{ $business->primary_contact_first_name }} {{ $business->primary_contact_last_name }}
|
||||
@endif
|
||||
|
||||
@if($business->primary_contact_email)
|
||||
**Email:** {{ $business->primary_contact_email }}
|
||||
@endif
|
||||
|
||||
@if($business->primary_contact_phone)
|
||||
**Phone:** {{ $business->primary_contact_phone }}
|
||||
@endif
|
||||
|
||||
## Accounts Payable Contact
|
||||
|
||||
@if($business->ap_contact_first_name || $business->ap_contact_last_name)
|
||||
**Name:** {{ $business->ap_contact_first_name }} {{ $business->ap_contact_last_name }}
|
||||
@endif
|
||||
|
||||
@if($business->ap_contact_email)
|
||||
**Email:** {{ $business->ap_contact_email }}
|
||||
@endif
|
||||
|
||||
@if($business->ap_contact_phone)
|
||||
**Phone:** {{ $business->ap_contact_phone }}
|
||||
@endif
|
||||
|
||||
@if($business->ap_preferred_contact_method)
|
||||
**Preferred Contact Method:** {{ ucfirst($business->ap_preferred_contact_method) }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_contact_first_name || $business->delivery_contact_last_name || $business->delivery_contact_phone)
|
||||
## Delivery Contact
|
||||
|
||||
@if($business->delivery_contact_first_name || $business->delivery_contact_last_name)
|
||||
**Name:** {{ $business->delivery_contact_first_name }} {{ $business->delivery_contact_last_name }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_contact_phone)
|
||||
**Phone:** {{ $business->delivery_contact_phone }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_contact_email)
|
||||
**Email:** {{ $business->delivery_contact_email }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_preferences)
|
||||
**Delivery Preferences:** {{ $business->delivery_preferences }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_directions)
|
||||
**Delivery Directions:** {{ $business->delivery_directions }}
|
||||
@endif
|
||||
|
||||
@if($business->delivery_schedule)
|
||||
**Delivery Schedule:** {{ $business->delivery_schedule }}
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@if($business->payment_method)
|
||||
## Payment Information
|
||||
|
||||
**Payment Method:** {{ ucfirst(str_replace('_', ' ', $business->payment_method)) }}
|
||||
@endif
|
||||
|
||||
## Compliance Documents
|
||||
|
||||
@if($business->w9_form_path)
|
||||
- W9 Form: Uploaded ✓
|
||||
@else
|
||||
- W9 Form: Not uploaded
|
||||
@endif
|
||||
|
||||
@if($business->resale_certificate_path)
|
||||
- Resale Certificate: Uploaded ✓
|
||||
@else
|
||||
- Resale Certificate: Not uploaded
|
||||
@endif
|
||||
|
||||
@if($business->ato_document_path)
|
||||
- ATO Document: Uploaded ✓
|
||||
@else
|
||||
- ATO Document: Not uploaded
|
||||
@endif
|
||||
|
||||
@if($business->tpt_document_path)
|
||||
- TPT Document: Uploaded ✓
|
||||
@else
|
||||
- TPT Document: Not uploaded
|
||||
@endif
|
||||
|
||||
@if($business->form_5000a_path)
|
||||
- Form 5000A: Uploaded ✓
|
||||
@else
|
||||
- Form 5000A: Not uploaded
|
||||
@endif
|
||||
|
||||
@if($business->notes)
|
||||
## Additional Notes
|
||||
|
||||
{{ $business->notes }}
|
||||
@endif
|
||||
|
||||
---
|
||||
|
||||
Please review this application and approve or reject it through the admin panel.
|
||||
|
||||
@component('mail::button', ['url' => route('filament.admin.resources.businesses.edit', $business)])
|
||||
Review & Approve Application
|
||||
@endcomponent
|
||||
|
||||
{{ config('app.name') }}
|
||||
@endcomponent
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="w-full">
|
||||
<iframe
|
||||
srcdoc="{{ $html }}"
|
||||
class="w-full border-2 border-gray-200 dark:border-gray-700 rounded-lg bg-white"
|
||||
style="min-height: 600px; width: 100%;"
|
||||
></iframe>
|
||||
</div>
|
||||
Reference in New Issue
Block a user