WIP: push notification when admin approves a deal

This commit is contained in:
kusowl 2026-02-06 19:01:53 +05:30
parent 5cae04884a
commit 82715973dc
30 changed files with 1454 additions and 200 deletions

View File

@ -70,3 +70,6 @@ TWILIO_NUMBER=
# OTP valid time in minutes # OTP valid time in minutes
OTP_LIFESPAN=10 OTP_LIFESPAN=10
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=

View File

@ -0,0 +1,26 @@
<?php
namespace App\Actions;
use App\Models\Broker;
use App\Models\Customer;
use App\Models\Deal;
use App\Models\Follow;
use App\Notifications\NewDealNotification;
final readonly class SendDealCreatedNotificationCustomerAction
{
public function execute(Deal $deal): void
{
/**
* @var Broker $broker
*/
$broker = $deal->broker->type;
$followers = $broker->followers()->with('user')->get();
$followers->map(function (Customer $customer) use ($deal) {
\Log::info('dump', [$customer]);
\Log::info("Sending notification to {$customer->user->name}");
$customer->user->notify(new NewDealNotification($deal));
});
}
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Actions\SendDealCreatedNotificationCustomerAction;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Deal; use App\Models\Deal;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -15,14 +16,14 @@ public function index()
->with('activeDeals', $this->activeDeals()); ->with('activeDeals', $this->activeDeals());
} }
public function approve(Deal $deal) public function approve(Deal $deal, SendDealCreatedNotificationCustomerAction $notificationCustomerAction)
{ {
try { try {
\DB::transaction(function () use ($deal) { \DB::transaction(function () use ($notificationCustomerAction, $deal) {
$deal->active = true; $deal->active = true;
$deal->save(); $deal->save();
$notificationCustomerAction->execute($deal);
}); });
return back()->with('success', 'Deal activated successfully.'); return back()->with('success', 'Deal activated successfully.');
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::error('Deal activation Failed: ', [$e->getMessage(), $e->getTrace()]); Log::error('Deal activation Failed: ', [$e->getMessage(), $e->getTrace()]);

View File

@ -0,0 +1,18 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\PushSubscriptionRequest;
use Illuminate\Http\JsonResponse;
class PushSubscriptionController extends Controller
{
public function __invoke(PushSubscriptionRequest $request): JsonResponse
{
$data = $request->validated();
$user = $request->user();
$user->updatePushSubscription($data['endpoint'], $data['keys']['p256dh'], $data['keys']['auth']);
return response()->json(['message' => 'Push subscription updated successfully.']);
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PushSubscriptionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'endpoint' => 'required|string|max:255',
'keys.auth' => 'required|string|max:255',
'keys.p256dh' => 'required|string|max:255',
];
}
}

View File

@ -3,7 +3,7 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\Relations\MorphOne;
/** /**
@ -45,8 +45,13 @@ public function user(): MorphOne
return $this->morphOne(User::class, 'role'); return $this->morphOne(User::class, 'role');
} }
public function followers(): HasMany public function followers(): BelongsToMany
{ {
return $this->hasMany(Follow::class, 'broker_id'); return $this->belongsToMany(
Customer::class,
'follows',
'broker_id',
'customer_id'
)->using(Follow::class);
} }
} }

View File

@ -4,9 +4,12 @@
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Pivot;
class Follow extends Model class Follow extends Pivot
{ {
protected $table = 'follows';
protected $fillable = [ protected $fillable = [
'customer_id', 'customer_id',
'broker_id', 'broker_id',

View File

@ -10,7 +10,7 @@
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use NotificationChannels\WebPush\HasPushSubscriptions;
/** /**
* @property int $id * @property int $id
* @property string $name * @property string $name
@ -62,7 +62,7 @@
class User extends Authenticatable class User extends Authenticatable
{ {
/** @use HasFactory<\Database\Factories\UserFactory> */ /** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable; use HasFactory, Notifiable, HasPushSubscriptions;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.

View File

@ -0,0 +1,40 @@
<?php
namespace App\Notifications;
use App\Models\Deal;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use NotificationChannels\WebPush\WebPushChannel;
use NotificationChannels\WebPush\WebPushMessage;
class NewDealNotification extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*/
public function __construct(private readonly Deal $deal)
{
//
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return [WebPushChannel::class];
}
public function toWebPush($notifiable, $notification): WebPushMessage
{
return (new WebPushMessage)
->title("New deal from {$this->deal->broker->name}")
->body("Check out this deal: {$this->deal->title}")
->action('View deal', route('explore', ['show' => $this->deal->id]));
}
}

View File

@ -1,2 +0,0 @@
*
!.gitignore

90
bootstrap/cache/packages.php vendored Normal file
View File

@ -0,0 +1,90 @@
<?php return array (
'barryvdh/laravel-ide-helper' =>
array (
'providers' =>
array (
0 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
),
),
'blade-ui-kit/blade-heroicons' =>
array (
'providers' =>
array (
0 => 'BladeUI\\Heroicons\\BladeHeroiconsServiceProvider',
),
),
'blade-ui-kit/blade-icons' =>
array (
'providers' =>
array (
0 => 'BladeUI\\Icons\\BladeIconsServiceProvider',
),
),
'laravel-notification-channels/webpush' =>
array (
'providers' =>
array (
0 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
),
),
'laravel/pail' =>
array (
'providers' =>
array (
0 => 'Laravel\\Pail\\PailServiceProvider',
),
),
'laravel/sail' =>
array (
'providers' =>
array (
0 => 'Laravel\\Sail\\SailServiceProvider',
),
),
'laravel/tinker' =>
array (
'providers' =>
array (
0 => 'Laravel\\Tinker\\TinkerServiceProvider',
),
),
'nesbot/carbon' =>
array (
'providers' =>
array (
0 => 'Carbon\\Laravel\\ServiceProvider',
),
),
'nunomaduro/collision' =>
array (
'providers' =>
array (
0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
),
),
'nunomaduro/termwind' =>
array (
'providers' =>
array (
0 => 'Termwind\\Laravel\\TermwindServiceProvider',
),
),
'opcodesio/log-viewer' =>
array (
'aliases' =>
array (
'LogViewer' => 'Opcodes\\LogViewer\\Facades\\LogViewer',
),
'providers' =>
array (
0 => 'Opcodes\\LogViewer\\LogViewerServiceProvider',
),
),
'pestphp/pest-plugin-laravel' =>
array (
'providers' =>
array (
0 => 'Pest\\Laravel\\PestServiceProvider',
),
),
);

279
bootstrap/cache/services.php vendored Normal file
View File

@ -0,0 +1,279 @@
<?php return array (
'providers' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
2 => 'Illuminate\\Bus\\BusServiceProvider',
3 => 'Illuminate\\Cache\\CacheServiceProvider',
4 => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
5 => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
6 => 'Illuminate\\Cookie\\CookieServiceProvider',
7 => 'Illuminate\\Database\\DatabaseServiceProvider',
8 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
9 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
10 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
11 => 'Illuminate\\Hashing\\HashServiceProvider',
12 => 'Illuminate\\Mail\\MailServiceProvider',
13 => 'Illuminate\\Notifications\\NotificationServiceProvider',
14 => 'Illuminate\\Pagination\\PaginationServiceProvider',
15 => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
16 => 'Illuminate\\Pipeline\\PipelineServiceProvider',
17 => 'Illuminate\\Queue\\QueueServiceProvider',
18 => 'Illuminate\\Redis\\RedisServiceProvider',
19 => 'Illuminate\\Session\\SessionServiceProvider',
20 => 'Illuminate\\Translation\\TranslationServiceProvider',
21 => 'Illuminate\\Validation\\ValidationServiceProvider',
22 => 'Illuminate\\View\\ViewServiceProvider',
23 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
24 => 'BladeUI\\Heroicons\\BladeHeroiconsServiceProvider',
25 => 'BladeUI\\Icons\\BladeIconsServiceProvider',
26 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
27 => 'Laravel\\Pail\\PailServiceProvider',
28 => 'Laravel\\Sail\\SailServiceProvider',
29 => 'Laravel\\Tinker\\TinkerServiceProvider',
30 => 'Carbon\\Laravel\\ServiceProvider',
31 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
32 => 'Termwind\\Laravel\\TermwindServiceProvider',
33 => 'Opcodes\\LogViewer\\LogViewerServiceProvider',
34 => 'Pest\\Laravel\\PestServiceProvider',
35 => 'App\\Providers\\AppServiceProvider',
),
'eager' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Cookie\\CookieServiceProvider',
2 => 'Illuminate\\Database\\DatabaseServiceProvider',
3 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
4 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
5 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
6 => 'Illuminate\\Notifications\\NotificationServiceProvider',
7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
8 => 'Illuminate\\Session\\SessionServiceProvider',
9 => 'Illuminate\\View\\ViewServiceProvider',
10 => 'BladeUI\\Heroicons\\BladeHeroiconsServiceProvider',
11 => 'BladeUI\\Icons\\BladeIconsServiceProvider',
12 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
13 => 'Laravel\\Pail\\PailServiceProvider',
14 => 'Carbon\\Laravel\\ServiceProvider',
15 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
16 => 'Termwind\\Laravel\\TermwindServiceProvider',
17 => 'Opcodes\\LogViewer\\LogViewerServiceProvider',
18 => 'Pest\\Laravel\\PestServiceProvider',
19 => 'App\\Providers\\AppServiceProvider',
),
'deferred' =>
array (
'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Contracts\\Broadcasting\\Broadcaster' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider',
'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider',
'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Concurrency\\Console\\InvokeSerializedClosureCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\PruneStaleTagsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PauseCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ResumeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ReloadCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleInterruptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StorageUnlinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ApiInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\BroadcastingInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ChannelListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ClassMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnumMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\InterfaceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\JobMiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\LangPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\TraitMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Migrations\\Migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Concurrency\\ConcurrencyManager' => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
'hash' => 'Illuminate\\Hashing\\HashServiceProvider',
'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider',
'mail.manager' => 'Illuminate\\Mail\\MailServiceProvider',
'mailer' => 'Illuminate\\Mail\\MailServiceProvider',
'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider',
'auth.password' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
'auth.password.broker' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
'pipeline' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
'queue' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.listener' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.worker' => 'Illuminate\\Queue\\QueueServiceProvider',
'redis' => 'Illuminate\\Redis\\RedisServiceProvider',
'redis.connection' => 'Illuminate\\Redis\\RedisServiceProvider',
'translator' => 'Illuminate\\Translation\\TranslationServiceProvider',
'translation.loader' => 'Illuminate\\Translation\\TranslationServiceProvider',
'validator' => 'Illuminate\\Validation\\ValidationServiceProvider',
'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => 'Illuminate\\Validation\\ValidationServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\GeneratorCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\ModelsCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\MetaCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Barryvdh\\LaravelIdeHelper\\Console\\EloquentCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
'Laravel\\Sail\\Console\\InstallCommand' => 'Laravel\\Sail\\SailServiceProvider',
'Laravel\\Sail\\Console\\PublishCommand' => 'Laravel\\Sail\\SailServiceProvider',
'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
),
'when' =>
array (
'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
array (
),
'Illuminate\\Bus\\BusServiceProvider' =>
array (
),
'Illuminate\\Cache\\CacheServiceProvider' =>
array (
),
'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider' =>
array (
),
'Illuminate\\Concurrency\\ConcurrencyServiceProvider' =>
array (
),
'Illuminate\\Hashing\\HashServiceProvider' =>
array (
),
'Illuminate\\Mail\\MailServiceProvider' =>
array (
),
'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider' =>
array (
),
'Illuminate\\Pipeline\\PipelineServiceProvider' =>
array (
),
'Illuminate\\Queue\\QueueServiceProvider' =>
array (
),
'Illuminate\\Redis\\RedisServiceProvider' =>
array (
),
'Illuminate\\Translation\\TranslationServiceProvider' =>
array (
),
'Illuminate\\Validation\\ValidationServiceProvider' =>
array (
),
'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider' =>
array (
),
'Laravel\\Sail\\SailServiceProvider' =>
array (
),
'Laravel\\Tinker\\TinkerServiceProvider' =>
array (
),
),
);

View File

@ -1,94 +1,95 @@
{ {
"$schema": "https://getcomposer.org/schema.json", "$schema": "https://getcomposer.org/schema.json",
"name": "laravel/laravel", "name": "laravel/laravel",
"type": "project", "type": "project",
"description": "The skeleton application for the Laravel framework.", "description": "The skeleton application for the Laravel framework.",
"keywords": [ "keywords": [
"laravel", "laravel",
"framework" "framework"
],
"license": "MIT",
"require": {
"php": "^8.2",
"blade-ui-kit/blade-heroicons": "^2.6",
"laravel-notification-channels/webpush": "^10.4",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1",
"twilio/sdk": "^8.10"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^3.6",
"fakerphp/faker": "^1.23",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.24",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"opcodesio/log-viewer": "^3.21",
"pestphp/pest": "^4.3",
"pestphp/pest-plugin-laravel": "^4.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"setup": [
"composer install",
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"@php artisan key:generate",
"@php artisan migrate --force",
"npm install",
"npm run build"
], ],
"license": "MIT", "dev": [
"require": { "Composer\\Config::disableProcessTimeout",
"php": "^8.2", "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
"blade-ui-kit/blade-heroicons": "^2.6", ],
"laravel/framework": "^12.0", "test": [
"laravel/tinker": "^2.10.1", "@php artisan config:clear --ansi",
"twilio/sdk": "^8.10", "@php artisan test"
"ext-cassandra": "*" ],
}, "post-autoload-dump": [
"require-dev": { "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"barryvdh/laravel-ide-helper": "^3.6", "@php artisan package:discover --ansi"
"fakerphp/faker": "^1.23", ],
"laravel/pail": "^1.2.2", "post-update-cmd": [
"laravel/pint": "^1.24", "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
"laravel/sail": "^1.41", ],
"mockery/mockery": "^1.6", "post-root-package-install": [
"nunomaduro/collision": "^8.6", "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
"pestphp/pest": "^4.3", ],
"pestphp/pest-plugin-laravel": "^4.0" "post-create-project-cmd": [
}, "@php artisan key:generate --ansi",
"autoload": { "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"psr-4": { "@php artisan migrate --graceful --ansi"
"App\\": "app/", ],
"Database\\Factories\\": "database/factories/", "pre-package-uninstall": [
"Database\\Seeders\\": "database/seeders/" "Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
} ]
}, },
"autoload-dev": { "extra": {
"psr-4": { "laravel": {
"Tests\\": "tests/" "dont-discover": []
} }
}, },
"scripts": { "config": {
"setup": [ "optimize-autoloader": true,
"composer install", "preferred-install": "dist",
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"", "sort-packages": true,
"@php artisan key:generate", "allow-plugins": {
"@php artisan migrate --force", "pestphp/pest-plugin": true,
"npm install", "php-http/discovery": true
"npm run build" }
], },
"dev": [ "minimum-stability": "stable",
"Composer\\Config::disableProcessTimeout", "prefer-stable": true
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others"
],
"test": [
"@php artisan config:clear --ansi",
"@php artisan test"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
],
"pre-package-uninstall": [
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
} }

738
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::connection(config('webpush.database_connection'))->create(config('webpush.table_name'), function (Blueprint $table) {
$table->bigIncrements('id');
$table->morphs('subscribable', 'push_subscriptions_subscribable_morph_idx');
$table->string('endpoint', 500)->unique();
$table->string('public_key')->nullable();
$table->string('auth_token')->nullable();
$table->string('content_encoding')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::connection(config('webpush.database_connection'))->dropIfExists(config('webpush.table_name'));
}
};

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "DealHub", "name": "dealhub",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

20
public/service-worker.js Normal file
View File

@ -0,0 +1,20 @@
self.addEventListener('push', function (e) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
//notifications aren't supported or permission not granted!
return;
}
if (e.data) {
let msg = '';
try {
msg = e.data.json();
}catch (e){
msg = e.data;
}
console.log(msg)
e.waitUntil(self.registration.showNotification(msg.title, {
body: msg.body,
actions: msg.actions
}));
}
});

1
public/vendor/log-viewer/app.css vendored Normal file

File diff suppressed because one or more lines are too long

2
public/vendor/log-viewer/app.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*!
* pinia v2.2.2
* (c) 2024 Eduardo San Martin Morote
* @license MIT
*/
/*! #__NO_SIDE_EFFECTS__ */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/**
* @vue/shared v3.4.38
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
**/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

View File

@ -0,0 +1,7 @@
{
"/app.js": "/app.js?id=b5eb6497b80ecd00237a857b35fcc1d6",
"/app.css": "/app.css?id=bf9e77abce3da8caacd004d57e4e8429",
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"
}

View File

@ -1,4 +1,5 @@
import {showToast} from "./toast.js"; import {showToast} from "./toast.js";
import {initSw} from "./webpush/enable-push.js";
/** /**
* Like a deal * Like a deal
@ -81,6 +82,10 @@ export async function follow(button, brokerId) {
let response = await axios.post(`/api/follow/${brokerId}`); let response = await axios.post(`/api/follow/${brokerId}`);
showToast(response.data.message); showToast(response.data.message);
if (!isFollowed) {
initSw();
}
} catch (e) { } catch (e) {
button.dataset.followed = isFollowed ? 'true' : 'false'; button.dataset.followed = isFollowed ? 'true' : 'false';
showToast('Something went wrong!') showToast('Something went wrong!')

View File

@ -0,0 +1,90 @@
import axios from 'axios';
export const initSw = async () => {
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
console.warn('Push messaging is not supported in this browser.');
return;
}
try {
const registration = await navigator.serviceWorker.register('/service-worker.js');
console.log('Service Worker Registered');
await initPush();
} catch (err) {
console.error('Service Worker registration failed:', err);
}
}
/**
* Request permission and trigger subscription
*/
const initPush = async () => {
// Request permission
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
console.warn('Notification permission denied.');
return;
}
await subscribeUser();
}
/**
* Subscribe the user to the Push Manager
*/
const subscribeUser = async () => {
const registration = await navigator.serviceWorker.ready;
// Check if a subscription already exists
let pushSubscription = await registration.pushManager.getSubscription();
if (!pushSubscription) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
import.meta.env.VITE_VAPID_PUBLIC_KEY
)
};
try {
pushSubscription = await registration.pushManager.subscribe(subscribeOptions);
console.log('New Subscription created.');
} catch (err) {
console.error('Failed to subscribe user:', err);
return;
}
} else {
console.log('User is already subscribed.');
}
// Send subscription to Laravel backend
if (pushSubscription) {
console.log('Received PushSubscription:', JSON.stringify(pushSubscription));
await storePushSubscription(pushSubscription);
}
}
export const storePushSubscription = async (pushSubscription) => {
try {
const response = await axios.post('/api/push', pushSubscription);
console.log('Successfully stored subscription:', response.data);
} catch (err) {
console.error('Error storing subscription on server:', err);
}
}
/**
* Helper to convert VAPID key
*/
function urlBase64ToUint8Array(base64String) {
if (!base64String) {
console.error('VITE_VAPID_PUBLIC_KEY is missing from .env');
return new Uint8Array();
}
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}

View File

@ -9,6 +9,7 @@
include __DIR__.'/interactions.php'; include __DIR__.'/interactions.php';
include __DIR__.'/deals.php'; include __DIR__.'/deals.php';
include __DIR__.'/stats/stats.php'; include __DIR__.'/stats/stats.php';
include __DIR__.'/push-notification.php';
Route::delete('/recent-search/{recentSearch}', RecentSearchController::class)->name('recent-search.destroy'); Route::delete('/recent-search/{recentSearch}', RecentSearchController::class)->name('recent-search.destroy');
}); });

View File

@ -0,0 +1,5 @@
<?php
use App\Http\Controllers\PushSubscriptionController;
Route::post('/push', PushSubscriptionController::class)->middleware('auth')->name('push-notification.subscribe');

View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -5,7 +5,19 @@ import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
laravel({ laravel({
input: ['resources/css/app.css', 'resources/js/app.js'], input: ['resources/css/app.css',
'resources/js/app.js',
'resources/js/sidebar.js',
'resources/js/modal.js',
'resources/js/admin-deals.js',
'resources/js/explore-page.js',
'resources/js/report-deal.js',
'resources/js/interaction.js',
'resources/js/tab.js',
'resources/js/comments.js',
'resources/js/alert.js',
'resources/js/webpush/enable-push.js',
],
refresh: true, refresh: true,
}), }),
tailwindcss(), tailwindcss(),