Compare commits
60 Commits
feature/us
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2132f8c02 | ||
|
|
44b13a5ebf | ||
|
|
964d7a8936 | ||
|
|
12726c93b5 | ||
|
|
82d61e757f | ||
|
|
59b74a6905 | ||
|
|
a853b58f48 | ||
|
|
587893511c | ||
|
|
976ea01c89 | ||
|
|
2f566cc4d8 | ||
|
|
0d0818baf3 | ||
|
|
82715973dc | ||
|
|
5cae04884a | ||
|
|
a06fac4fef | ||
|
|
aa7e2f245f | ||
|
|
2fecd52d7e | ||
|
|
d7c06c38a6 | ||
|
|
1edfd7b9d4 | ||
|
|
aa8ad6b84b | ||
|
|
d03aa31f30 | ||
|
|
0b631f6049 | ||
|
|
039f8f5568 | ||
|
|
38d429e5d5 | ||
|
|
450c8b4dce | ||
|
|
a8be44553e | ||
|
|
0789c21100 | ||
|
|
2590ebc0ab | ||
|
|
6ede0d4548 | ||
|
|
298d73791f | ||
|
|
c087126080 | ||
|
|
aa3056e1d1 | ||
|
|
c1ff760282 | ||
|
|
a4f644ad20 | ||
|
|
690a50408d | ||
|
|
91a11c8f56 | ||
|
|
4fd98957cb | ||
|
|
1442856fb4 | ||
|
|
62c306986c | ||
|
|
94ef8f360d | ||
|
|
a5f2f43fb1 | ||
|
|
45196cba56 | ||
|
|
4f34c5b595 | ||
|
|
193913dfad | ||
|
|
4060866b2d | ||
|
|
1d9f3c5c04 | ||
|
|
df4e82a160 | ||
|
|
9e7fda4ea2 | ||
|
|
3cd2644582 | ||
|
|
9e61dd9f51 | ||
|
|
6b52aed66a | ||
|
|
f14f7ece54 | ||
|
|
db012f2998 | ||
|
|
4741c894e6 | ||
|
|
985dd967e4 | ||
|
|
673915887c | ||
|
|
6c7f411946 | ||
|
|
e5ebe21ed1 | ||
|
|
af6d629b68 | ||
|
|
688fd02e26 | ||
|
|
89271220fa |
27
.env.example
27
.env.example
@ -1,4 +1,4 @@
|
|||||||
APP_NAME=Laravel
|
APP_NAME=DealHub
|
||||||
APP_ENV=local
|
APP_ENV=local
|
||||||
APP_KEY=
|
APP_KEY=
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
@ -63,3 +63,28 @@ AWS_BUCKET=
|
|||||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||||
|
|
||||||
VITE_APP_NAME="${APP_NAME}"
|
VITE_APP_NAME="${APP_NAME}"
|
||||||
|
|
||||||
|
TWILIO_SID=
|
||||||
|
TWILIO_AUTH_TOKEN=
|
||||||
|
TWILIO_NUMBER=
|
||||||
|
|
||||||
|
# OTP valid time in minutes
|
||||||
|
OTP_LIFESPAN=10
|
||||||
|
|
||||||
|
VAPID_PUBLIC_KEY=
|
||||||
|
VAPID_PRIVATE_KEY=
|
||||||
|
|
||||||
|
# Same as the VAPID_PUBLIC_KEY
|
||||||
|
VITE_VAPID_PUBLIC_KEY="${VAPID_PUBLIC_KEY}"
|
||||||
|
|
||||||
|
REVERB_APP_ID=
|
||||||
|
REVERB_APP_KEY=
|
||||||
|
REVERB_APP_SECRET=
|
||||||
|
REVERB_HOST=
|
||||||
|
REVERB_PORT=
|
||||||
|
REVERB_SCHEME=
|
||||||
|
|
||||||
|
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
|
||||||
|
VITE_REVERB_HOST="${REVERB_HOST}"
|
||||||
|
VITE_REVERB_PORT="${REVERB_PORT}"
|
||||||
|
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
|
||||||
|
|||||||
2371
.phpstorm.meta.php
Normal file
2371
.phpstorm.meta.php
Normal file
File diff suppressed because it is too large
Load Diff
11
README.md
11
README.md
@ -79,11 +79,22 @@ ## ⚙️ Installation
|
|||||||
```bash
|
```bash
|
||||||
git clone https://git.sentientgeeks.us/joydip.manna/dealhub.git
|
git clone https://git.sentientgeeks.us/joydip.manna/dealhub.git
|
||||||
cd dealhub
|
cd dealhub
|
||||||
|
# Install dependencies
|
||||||
composer install
|
composer install
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
|
# Set VAPID for webpush
|
||||||
|
php artisan webpush:vapid
|
||||||
|
|
||||||
|
# Install Reverb
|
||||||
|
php artisan install:broadcasting
|
||||||
|
|
||||||
npm run dev
|
npm run dev
|
||||||
php artisan migrate
|
php artisan migrate
|
||||||
php artisan serve
|
php artisan serve
|
||||||
|
|
||||||
|
# Start Reverb Server ( Another terminal )
|
||||||
|
php artisan reverb:start
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
4
TODO.md
Normal file
4
TODO.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
- [ ] Live Support.
|
||||||
|
- [ ] Add an Audit Log table to save all actions.
|
||||||
|
- [ ] Link foreign keys properly with related tables in the database.
|
||||||
|
- [ ] Add debouncing to the search feature and create proper database indexes to improve search performance.
|
||||||
28340
_ide_helper.php
Normal file
28340
_ide_helper.php
Normal file
File diff suppressed because it is too large
Load Diff
30
app/Actions/AddRecentSearchAction.php
Normal file
30
app/Actions/AddRecentSearchAction.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
final readonly class AddRecentSearchAction
|
||||||
|
{
|
||||||
|
public function execute(User $user, array $data): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($user, $data) {
|
||||||
|
$user->recentSearches()->updateOrcreate($data);
|
||||||
|
$recentSearchCount = $user->recentSearches()->count();
|
||||||
|
if ($recentSearchCount > 5) {
|
||||||
|
$user->recentSearches()->oldest()->limit(1)->delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error adding recent search',
|
||||||
|
[
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTrace(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Actions/CreateOrGetInboxAction.php
Normal file
31
app/Actions/CreateOrGetInboxAction.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\Inbox;
|
||||||
|
use App\Models\User;
|
||||||
|
use DB;
|
||||||
|
|
||||||
|
final readonly class CreateOrGetInboxAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(User $recipient, User $sender): Inbox
|
||||||
|
{
|
||||||
|
$existingInbox = Inbox::whereHas('users', fn ($q) => $q->where('users.id', $sender->id))
|
||||||
|
->whereHas('users', fn ($q) => $q->where('users.id', $recipient->id))
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($existingInbox) {
|
||||||
|
return $existingInbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($sender, $recipient) {
|
||||||
|
$inbox = Inbox::create();
|
||||||
|
$inbox->users()->attach([$sender->id, $recipient->id]);
|
||||||
|
|
||||||
|
return $inbox;
|
||||||
|
}, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Actions/GetAdminStatsAction.php
Normal file
22
app/Actions/GetAdminStatsAction.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Enums\UserTypes;
|
||||||
|
use App\Models\Deal;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class GetAdminStatsAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, int>
|
||||||
|
*/
|
||||||
|
public function execute(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'listings' => Deal::count(),
|
||||||
|
'customers' => User::where('role', UserTypes::User->value)->count(),
|
||||||
|
'brokers' => User::where('role', UserTypes::Broker->value)->count(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Actions/GetBrokerStatsAction.php
Normal file
22
app/Actions/GetBrokerStatsAction.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Enums\InteractionType;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class GetBrokerStatsAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, int>
|
||||||
|
*/
|
||||||
|
public function execute(User $user): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'listings' => $user->deals()->count(),
|
||||||
|
'likes' => $user->dealsInteractions()->where('type', InteractionType::Like)->count(),
|
||||||
|
'views' => $user->dealsInteractions()->where('type', InteractionType::View)->sum('count'),
|
||||||
|
'clicks' => $user->dealsInteractions()->where('type', InteractionType::Redirection)->sum('count'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Actions/GetUserFavoritesAction.php
Normal file
20
app/Actions/GetUserFavoritesAction.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Enums\InteractionType;
|
||||||
|
use App\Models\Deal;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
final readonly class GetUserFavoritesAction
|
||||||
|
{
|
||||||
|
public function execute(User $user): Collection
|
||||||
|
{
|
||||||
|
return $user->interactedDeals()
|
||||||
|
->where('interactions.type', InteractionType::Favorite)
|
||||||
|
->tap(fn ($q) => (new Deal)->withActiveDeals($q))
|
||||||
|
->select('deals.id', 'deals.title')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Actions/GetUserReportedDealsAction.php
Normal file
17
app/Actions/GetUserReportedDealsAction.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
final readonly class GetUserReportedDealsAction
|
||||||
|
{
|
||||||
|
public function execute(User $user): Collection
|
||||||
|
{
|
||||||
|
return $user->reports()
|
||||||
|
->select('reports.id')
|
||||||
|
->with('deals:id,title')
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Actions/PasswordReset/ResendOTPAction.php
Normal file
29
app/Actions/PasswordReset/ResendOTPAction.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\PasswordReset;
|
||||||
|
|
||||||
|
use App\Exceptions\UserNotFoundException;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\OTPService;
|
||||||
|
|
||||||
|
final readonly class ResendOTPAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private OTPService $otpService,
|
||||||
|
private SendOTPToUserAction $otpToUserAction
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(): void
|
||||||
|
{
|
||||||
|
$user = \Session::get('otp_user_id') ? User::find(\Session::get('otp_user_id')) : null;
|
||||||
|
throw_if(! $user, new UserNotFoundException('User not found'));
|
||||||
|
|
||||||
|
$otp = $this->otpService->generate($user);
|
||||||
|
|
||||||
|
$this->otpToUserAction->execute(['user' => $user, 'otp' => $otp]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Actions/PasswordReset/SendOTPAction.php
Normal file
30
app/Actions/PasswordReset/SendOTPAction.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\PasswordReset;
|
||||||
|
|
||||||
|
use App\Exceptions\UserNotFoundException;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\OTPService;
|
||||||
|
|
||||||
|
final readonly class SendOTPAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private OTPService $otpService,
|
||||||
|
private SendOTPToUserAction $otpToUserAction
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data): void
|
||||||
|
{
|
||||||
|
$user = User::where('email', $data['email'])->first();
|
||||||
|
throw_if(! $user, new UserNotFoundException('User not found'));
|
||||||
|
|
||||||
|
\Session::put('otp_user_id', $user->id);
|
||||||
|
$otp = $this->otpService->generate($user);
|
||||||
|
|
||||||
|
$this->otpToUserAction->execute(['user' => $user, 'otp' => $otp]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Actions/PasswordReset/SendOTPToUserAction.php
Normal file
35
app/Actions/PasswordReset/SendOTPToUserAction.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\PasswordReset;
|
||||||
|
|
||||||
|
use App\Actions\SendPasswordResetMailAction;
|
||||||
|
use App\Services\TwilioService;
|
||||||
|
use Twilio\Exceptions\TwilioException;
|
||||||
|
|
||||||
|
final readonly class SendOTPToUserAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private SendPasswordResetMailAction $mailAction,
|
||||||
|
private TwilioService $twilioService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
*
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data): void
|
||||||
|
{
|
||||||
|
['user' => $user,'otp' => $otp] = $data;
|
||||||
|
|
||||||
|
$this->mailAction->execute($user->email, $otp);
|
||||||
|
|
||||||
|
if ($user?->type->phone !== null) {
|
||||||
|
try {
|
||||||
|
$this->twilioService->sendSms($user->type->phone, "Your OTP is $otp");
|
||||||
|
} catch (TwilioException $e) {
|
||||||
|
\Log::error('SMS send failed', [$e->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
app/Actions/PasswordReset/VerifyOTPAction.php
Normal file
25
app/Actions/PasswordReset/VerifyOTPAction.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\PasswordReset;
|
||||||
|
|
||||||
|
use App\Exceptions\UserNotFoundException;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\OTPService;
|
||||||
|
|
||||||
|
final readonly class VerifyOTPAction
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private OTPService $otpService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data): bool
|
||||||
|
{
|
||||||
|
$user = \Session::get('otp_user_id') ? User::find(\Session::get('otp_user_id')) : null;
|
||||||
|
throw_if(! $user, new UserNotFoundException('User not found'));
|
||||||
|
|
||||||
|
return $this->otpService->verify($user, $data['otp']);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
app/Actions/RecordUserPageVisitAction.php
Normal file
14
app/Actions/RecordUserPageVisitAction.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\PageVisit;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class RecordUserPageVisitAction
|
||||||
|
{
|
||||||
|
public function execute(?User $user, string $page): void
|
||||||
|
{
|
||||||
|
PageVisit::create(['user_id' => $user?->id, 'page' => $page, 'user_type' => $user?->role ?? null, 'created_at' => now()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/Actions/SendDealCreatedNotificationCustomerAction.php
Normal file
27
app/Actions/SendDealCreatedNotificationCustomerAction.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\Broker;
|
||||||
|
use App\Models\Customer;
|
||||||
|
use App\Models\Deal;
|
||||||
|
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 $follower) use ($deal) {
|
||||||
|
$user = $follower->user;
|
||||||
|
|
||||||
|
\Log::info("Sending notification to {$follower->user->name}", [$deal, $follower->user]);
|
||||||
|
|
||||||
|
$user->notifyNow(new NewDealNotification($deal));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Actions/SendMessageAction.php
Normal file
45
app/Actions/SendMessageAction.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Events\MessageSent;
|
||||||
|
use App\Exceptions\MessageNotSendException;
|
||||||
|
use App\Models\User;
|
||||||
|
use DB;
|
||||||
|
|
||||||
|
final readonly class SendMessageAction
|
||||||
|
{
|
||||||
|
public function __construct(private CreateOrGetInboxAction $inboxAction) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(User $sender, User $recipient, array $data): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
// find the inbox between the two users
|
||||||
|
DB::beginTransaction();
|
||||||
|
$inbox = $this->inboxAction->execute($recipient, $sender);
|
||||||
|
|
||||||
|
// update the inbox with the last message and last user as current user
|
||||||
|
$inbox->last_message = $data['message'];
|
||||||
|
$inbox->last_user_id = $sender->id;
|
||||||
|
$inbox->save();
|
||||||
|
|
||||||
|
// create a new message in the inbox
|
||||||
|
$message = $inbox->messages()->create([
|
||||||
|
'message' => $data['message'],
|
||||||
|
'user_id' => $sender->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Send the message to all other users in the inbox
|
||||||
|
broadcast(new MessageSent($message))->toOthers();
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
throw new MessageNotSendException('Message not sent.', previous: $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/Actions/SendPasswordResetMailAction.php
Normal file
21
app/Actions/SendPasswordResetMailAction.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Mail\PasswordResetMail;
|
||||||
|
|
||||||
|
final readonly class SendPasswordResetMailAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function execute(string $email, string $otp): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$message = \Mail::to($email)->send(new PasswordResetMail($otp));
|
||||||
|
\Log::info('Mail sent successfully', ['message' => $message]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::info('Mail send failed', ['message' => $e->getMessage()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Actions/UpdateBrokerAction.php
Normal file
30
app/Actions/UpdateBrokerAction.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
final readonly class UpdateBrokerAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data, User $profile): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Separate the user fields from the broker fields
|
||||||
|
*/
|
||||||
|
$userFields = ['name', 'email'];
|
||||||
|
$data = collect($data);
|
||||||
|
$profileData = $data->only($userFields)->toArray();
|
||||||
|
$userData = $data->except($userFields)->toArray();
|
||||||
|
|
||||||
|
DB::transaction(function () use ($profileData, $profile, $userData) {
|
||||||
|
$profile->update($profileData);
|
||||||
|
$user = $profile->type;
|
||||||
|
$user->update($userData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Actions/UpdateCustomerAction.php
Normal file
30
app/Actions/UpdateCustomerAction.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
final readonly class UpdateCustomerAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data, User $profile): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Separate the user fields from the broker fields
|
||||||
|
*/
|
||||||
|
$userFields = ['name', 'email'];
|
||||||
|
$data = collect($data);
|
||||||
|
$profileData = $data->only($userFields)->toArray();
|
||||||
|
$userData = $data->except($userFields)->toArray();
|
||||||
|
|
||||||
|
DB::transaction(function () use ($profileData, $profile, $userData) {
|
||||||
|
$profile->update($profileData);
|
||||||
|
$user = $profile->type;
|
||||||
|
$user->update($userData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
13
app/Enums/ExplorePageFilters.php
Normal file
13
app/Enums/ExplorePageFilters.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use App\Traits\EnumAsArray;
|
||||||
|
|
||||||
|
enum ExplorePageFilters: string
|
||||||
|
{
|
||||||
|
use EnumAsArray;
|
||||||
|
|
||||||
|
case Like = 'like';
|
||||||
|
case Click = 'click';
|
||||||
|
}
|
||||||
@ -11,4 +11,5 @@ enum InteractionType: string
|
|||||||
case Like = 'like';
|
case Like = 'like';
|
||||||
case Favorite = 'favorite';
|
case Favorite = 'favorite';
|
||||||
case Redirection = 'redirect';
|
case Redirection = 'redirect';
|
||||||
|
case View = 'view';
|
||||||
}
|
}
|
||||||
|
|||||||
38
app/Events/MessageSent.php
Normal file
38
app/Events/MessageSent.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events;
|
||||||
|
|
||||||
|
use App\Models\Message;
|
||||||
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class MessageSent implements ShouldBroadcastNow
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*/
|
||||||
|
public function __construct(public Message $message)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the channels the event should broadcast on.
|
||||||
|
*
|
||||||
|
* @return array<int, \Illuminate\Broadcasting\Channel>
|
||||||
|
*/
|
||||||
|
public function broadcastOn(): array
|
||||||
|
{
|
||||||
|
$users = $this->message->inbox->users->pluck('id')->toArray();
|
||||||
|
sort($users);
|
||||||
|
|
||||||
|
return [
|
||||||
|
new PrivateChannel("chat.{$users[0]}.{$users[1]}"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
7
app/Exceptions/MessageNotSendException.php
Normal file
7
app/Exceptions/MessageNotSendException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class MessageNotSendException extends Exception {}
|
||||||
7
app/Exceptions/UserNotFoundException.php
Normal file
7
app/Exceptions/UserNotFoundException.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class UserNotFoundException extends Exception {}
|
||||||
15
app/Http/Controllers/Admin/AdminDashboardController.php
Normal file
15
app/Http/Controllers/Admin/AdminDashboardController.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Actions\GetAdminStatsAction;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
class AdminDashboardController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(GetAdminStatsAction $action)
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.index')
|
||||||
|
->with('stats', $action->execute());
|
||||||
|
}
|
||||||
|
}
|
||||||
79
app/Http/Controllers/Admin/BrokerController.php
Normal file
79
app/Http/Controllers/Admin/BrokerController.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Actions\UpdateBrokerAction;
|
||||||
|
use App\Enums\UserStatus;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\StoreBrokerProfileRequest;
|
||||||
|
use App\Models\Broker;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class BrokerController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.brokers.index')
|
||||||
|
->with('activeBrokers', Broker::select(['id', 'location'])
|
||||||
|
->whereRelation('user', 'status', UserStatus::Active->value)
|
||||||
|
->with('user:id,name,email,role_id,role_type')
|
||||||
|
->get()
|
||||||
|
)
|
||||||
|
->with('pendingBrokers', Broker::select(['id', 'location'])
|
||||||
|
->whereRelation('user', 'status', UserStatus::Pending->value)
|
||||||
|
->with('user:id,name,email,role_id,role_type')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Broker $broker)
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.brokers.edit')
|
||||||
|
->with('profile', $broker->user)
|
||||||
|
->with('backLink', route('admin.brokers.index'))
|
||||||
|
->with('actionLink', route('admin.brokers.update', $broker));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(StoreBrokerProfileRequest $request, Broker $broker, UpdateBrokerAction $action)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$action->execute($request->validated(), $broker->user);
|
||||||
|
|
||||||
|
return to_route('admin.brokers.index')
|
||||||
|
->with('success', 'Profile updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Broker Profile Update Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->withInput()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Broker $broker)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
\DB::transaction(function () use ($broker) {
|
||||||
|
$broker->user->delete();
|
||||||
|
$broker->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('success', 'Broker deleted successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Broker Delete Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function approve(Broker $broker)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$broker->user->update(['status' => UserStatus::Active->value]);
|
||||||
|
|
||||||
|
return to_route('admin.brokers.index')->with('success', 'Broker approved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Broker Approval Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Http/Controllers/Admin/CustomerController.php
Normal file
59
app/Http/Controllers/Admin/CustomerController.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Actions\UpdateCustomerAction;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\StoreCustomerProfileRequest;
|
||||||
|
use App\Models\Customer;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class CustomerController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.customers.index')
|
||||||
|
->with('customers', Customer::select(['id', 'location'])
|
||||||
|
->with('user:id,name,email,role_id,role_type')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Customer $customer)
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.customers.edit')
|
||||||
|
->with('profile', $customer->user)
|
||||||
|
->with('backLink', route('admin.customers.index'))
|
||||||
|
->with('actionLink', route('admin.customers.update', $customer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(StoreCustomerProfileRequest $request, Customer $customer, UpdateCustomerAction $action)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$action->execute($request->validated(), $customer->user);
|
||||||
|
|
||||||
|
return to_route('admin.customers.index')
|
||||||
|
->with('success', 'Profile updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Customer Profile Update Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->withInput()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Customer $customer)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
\DB::transaction(function () use ($customer) {
|
||||||
|
$customer->user->delete();
|
||||||
|
$customer->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('success', 'Customer deleted successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Customer Delete Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
app/Http/Controllers/Admin/DealController.php
Normal file
58
app/Http/Controllers/Admin/DealController.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Actions\SendDealCreatedNotificationCustomerAction;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Deal;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class DealController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.deals.index')
|
||||||
|
->with('pendingDeals', $this->pendingDeals())
|
||||||
|
->with('activeDeals', $this->activeDeals());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function approve(Deal $deal, SendDealCreatedNotificationCustomerAction $notificationCustomerAction)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
\DB::transaction(function () use ($deal) {
|
||||||
|
$deal->active = true;
|
||||||
|
$deal->save();
|
||||||
|
});
|
||||||
|
$notificationCustomerAction->execute($deal);
|
||||||
|
|
||||||
|
return back()->with('success', 'Deal activated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Deal activation Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reject(Deal $deal)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$deal->delete();
|
||||||
|
|
||||||
|
return back()->with('success', 'Deal deleted successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Deal deletion Failed: ', [$e->getMessage(), $e->getTrace()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function pendingDeals()
|
||||||
|
{
|
||||||
|
return Deal::where('active', false)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function activeDeals()
|
||||||
|
{
|
||||||
|
return Deal::where('active', true)->get();
|
||||||
|
}
|
||||||
|
}
|
||||||
87
app/Http/Controllers/Admin/ReportController.php
Normal file
87
app/Http/Controllers/Admin/ReportController.php
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Enums\ReportStatus;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Report;
|
||||||
|
use App\Notifications\ReportRejectedNotificationToUser;
|
||||||
|
use App\Notifications\ReportResolvedNotificationToBroker;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class ReportController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$reports = Report::query()
|
||||||
|
->with(['user:id,name', 'deals:id,title'])
|
||||||
|
->tap(fn ($q) => (new Report)->orderByStatus($q,
|
||||||
|
[ReportStatus::Pending, ReportStatus::Rejected, ReportStatus::Resolved]))
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('dashboards.admin.reports.index')
|
||||||
|
->with('reports', $reports);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resolve(Report $report)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$report->status = ReportStatus::Resolved;
|
||||||
|
$report->save();
|
||||||
|
|
||||||
|
$report->user->notify(new ReportRejectedNotificationToUser($report->deals()->first()->title, false));
|
||||||
|
$report->deals()->first()->broker->notify(new ReportResolvedNotificationToBroker($report->deals()->first()->title,
|
||||||
|
false));
|
||||||
|
|
||||||
|
return back()->with('success', 'Report resolved successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::error('Error resolving report', [$report->id, $e->getMessage()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reject(Report $report)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$report->status = ReportStatus::Rejected;
|
||||||
|
$report->save();
|
||||||
|
|
||||||
|
$report->user->notify(new ReportRejectedNotificationToUser($report->deals()->first()->title));
|
||||||
|
|
||||||
|
return back()->with('success', 'Report Rejected successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::error('Error rejecting report', [$report->id, $e->getMessage()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the attached deal inactive and mark the report resolved.
|
||||||
|
*/
|
||||||
|
public function removeContent(Report $report)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($report) {
|
||||||
|
|
||||||
|
$report->user->notify(new ReportRejectedNotificationToUser($report->deals()->first()->title, true));
|
||||||
|
$report->deals()->first()->broker->notify(new ReportResolvedNotificationToBroker($report->deals()->first()->title,
|
||||||
|
true));
|
||||||
|
|
||||||
|
$deal = $report->deals()->first();
|
||||||
|
$deal->active = false;
|
||||||
|
$report->status = ReportStatus::Resolved;
|
||||||
|
|
||||||
|
$deal->save();
|
||||||
|
$report->save();
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('error', 'Deal has been deactivated.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::error('Error removing content of report', [$report->id, $e->getMessage(), $e->getTraceAsString()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Actions\RecordUserPageVisitAction;
|
||||||
use App\Enums\UserStatus;
|
use App\Enums\UserStatus;
|
||||||
use App\Enums\UserTypes;
|
use App\Enums\UserTypes;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\AuthenticateUserRequest;
|
use App\Http\Requests\AuthenticateUserRequest;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
@ -15,7 +17,7 @@ public function create()
|
|||||||
return view('auth.login');
|
return view('auth.login');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(AuthenticateUserRequest $request)
|
public function store(AuthenticateUserRequest $request, RecordUserPageVisitAction $action)
|
||||||
{
|
{
|
||||||
$data = $request->validated();
|
$data = $request->validated();
|
||||||
if (Auth::attempt($data, $data['remember_me'] ?? false)) {
|
if (Auth::attempt($data, $data['remember_me'] ?? false)) {
|
||||||
@ -34,6 +36,12 @@ public function store(AuthenticateUserRequest $request)
|
|||||||
UserTypes::Broker->value, UserTypes::User->value => 'explore',
|
UserTypes::Broker->value, UserTypes::User->value => 'explore',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
$action->execute($user, $route);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::error('Error recording user page visit', [$e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
||||||
return to_route($route);
|
return to_route($route);
|
||||||
} else {
|
} else {
|
||||||
return back()
|
return back()
|
||||||
32
app/Http/Controllers/Auth/ImpersonatedUserController.php
Normal file
32
app/Http/Controllers/Auth/ImpersonatedUserController.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Auth;
|
||||||
|
|
||||||
|
class ImpersonatedUserController extends Controller
|
||||||
|
{
|
||||||
|
public function store(User $user)
|
||||||
|
{
|
||||||
|
Session()->put('impersonate', Auth::id());
|
||||||
|
Session()->put('impersonate_name', $user->name);
|
||||||
|
|
||||||
|
Auth::login($user);
|
||||||
|
|
||||||
|
return to_route('explore');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy()
|
||||||
|
{
|
||||||
|
$adminId = Session()->get('impersonate');
|
||||||
|
|
||||||
|
Auth::loginUsingId($adminId);
|
||||||
|
|
||||||
|
Session()->forget('impersonate');
|
||||||
|
Session()->forget('impersonate_name');
|
||||||
|
|
||||||
|
return to_route('admin.dashboard');
|
||||||
|
}
|
||||||
|
}
|
||||||
91
app/Http/Controllers/Auth/PasswordResetController.php
Normal file
91
app/Http/Controllers/Auth/PasswordResetController.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Actions\PasswordReset\ResendOTPAction;
|
||||||
|
use App\Actions\PasswordReset\SendOTPAction;
|
||||||
|
use App\Actions\PasswordReset\VerifyOTPAction;
|
||||||
|
use App\Exceptions\UserNotFoundException;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Validation\Rules\Password;
|
||||||
|
|
||||||
|
class PasswordResetController extends Controller
|
||||||
|
{
|
||||||
|
public function show()
|
||||||
|
{
|
||||||
|
return view('auth.passwords.reset');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendCode(Request $request, SendOTPAction $action)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'email' => 'required|email',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$action->execute($data);
|
||||||
|
|
||||||
|
return to_route('password.reset.show.verify')
|
||||||
|
->with('success', 'Password reset code is sent');
|
||||||
|
} catch (UserNotFoundException $e) {
|
||||||
|
return to_route('password.reset.show.verify')->with('success', 'Password reset code is sent');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showVerify()
|
||||||
|
{
|
||||||
|
return view('auth.passwords.verify')
|
||||||
|
->with('expiryMinutes', 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verify(Request $request, VerifyOTPAction $otpAction)
|
||||||
|
{
|
||||||
|
$data = $request->validate(['otp' => 'required|string:min:5:max:6']);
|
||||||
|
try {
|
||||||
|
$isVerified = $otpAction->execute($data);
|
||||||
|
if (! $isVerified) {
|
||||||
|
return back()->with('error', 'Invalid OTP');
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_route('password.reset.show.update')->with('success', 'OTP Verified');
|
||||||
|
} catch (UserNotFoundException $e) {
|
||||||
|
return back()->with('error', 'Session Expired');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showUpdate()
|
||||||
|
{
|
||||||
|
return view('auth.passwords.update');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'password' => 'required', 'confirmed', Password::min(8)->letters()->mixedCase()->numbers()->symbols(),
|
||||||
|
]);
|
||||||
|
$user = User::find(Session::get('otp_user_id'));
|
||||||
|
if (! $user) {
|
||||||
|
return back()->with('error', 'Session Expired');
|
||||||
|
}
|
||||||
|
$user->update(['password' => $data['password']]);
|
||||||
|
|
||||||
|
\Session::forget('otp_user_id');
|
||||||
|
|
||||||
|
return to_route('login.create')->with('success', 'Password updated successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resend(ResendOTPAction $otpAction)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$otpAction->execute();
|
||||||
|
|
||||||
|
return to_route('password.reset.show.verify')
|
||||||
|
->with('success', 'Password reset code is sent');
|
||||||
|
} catch (UserNotFoundException $e) {
|
||||||
|
return to_route('password.reset.show.verify')->with('success', 'Password reset code is sent');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
use App\Enums\UserStatus;
|
use App\Enums\UserStatus;
|
||||||
use App\Enums\UserTypes;
|
use App\Enums\UserTypes;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\StoreRegisterdUser;
|
use App\Http\Requests\StoreRegisterdUser;
|
||||||
use App\Models\Broker;
|
use App\Models\Broker;
|
||||||
use App\Models\User;
|
use App\Models\Customer;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
@ -22,21 +23,29 @@ public function store(StoreRegisterdUser $request)
|
|||||||
$data = $request->validated();
|
$data = $request->validated();
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($data) {
|
DB::transaction(function () use ($data) {
|
||||||
if ($data['role'] === UserTypes::Broker->value) {
|
switch ($data['role']) {
|
||||||
|
case UserTypes::Broker->value:
|
||||||
|
|
||||||
$data['status'] = UserStatus::Pending->value;
|
$data['status'] = UserStatus::Pending->value;
|
||||||
|
|
||||||
// Create Broker first, then the User linked to it
|
// Create Broker first, then link the user
|
||||||
$broker = Broker::create();
|
$broker = Broker::create();
|
||||||
$broker->user()->create($data);
|
$broker->user()->create($data);
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case UserTypes::User->value:
|
||||||
|
|
||||||
$data['status'] = UserStatus::Active->value;
|
$data['status'] = UserStatus::Active->value;
|
||||||
|
|
||||||
User::create($data);
|
$customer = Customer::create();
|
||||||
|
$customer->user()->create($data);
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return to_route('login.create')
|
return to_route('login.create')
|
||||||
->with('userRegistered', 'User registered successfully.');
|
->with('success', 'User registered successfully.');
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::error('Registration Failed: '.$e->getMessage());
|
Log::error('Registration Failed: '.$e->getMessage());
|
||||||
@ -2,15 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Broker;
|
namespace App\Http\Controllers\Broker;
|
||||||
|
|
||||||
|
use App\Actions\GetBrokerStatsAction;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class BrokerDashboardController extends Controller
|
class BrokerDashboardController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index(GetBrokerStatsAction $getBrokerStatsAction)
|
||||||
{
|
{
|
||||||
return view('dashboards.broker.index')
|
return view('dashboards.broker.index')
|
||||||
->with('deals', $this->deals());
|
->with('deals', $this->deals())
|
||||||
|
->with('stats', $getBrokerStatsAction->execute(Auth::user()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function deals()
|
protected function deals()
|
||||||
@ -28,7 +30,9 @@ protected function deals()
|
|||||||
'deal_category_id',
|
'deal_category_id',
|
||||||
])
|
])
|
||||||
->with('category:id,name')
|
->with('category:id,name')
|
||||||
->latest()
|
->WithLikePerDeal()
|
||||||
->paginate();
|
->WithRedirectionPerDeal()
|
||||||
|
->withViewPerDeal()
|
||||||
|
->latest()->take(3)->get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers\Broker;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\StoreBrokerDeal;
|
use App\Http\Requests\StoreBrokerDeal;
|
||||||
use App\Models\Deal;
|
use App\Models\Deal;
|
||||||
use App\Models\DealCategory;
|
use App\Models\DealCategory;
|
||||||
use App\Services\FileService;
|
use App\Services\FileService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class BrokerDealController extends Controller
|
class BrokerDealController extends Controller
|
||||||
@ -18,7 +19,8 @@ class BrokerDealController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
//
|
return view('dashboards.broker.deals.index')
|
||||||
|
->with('deals', $this->deals());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +51,7 @@ public function store(StoreBrokerDeal $request, FileService $fileService)
|
|||||||
Deal::create($data);
|
Deal::create($data);
|
||||||
Deal::reguard();
|
Deal::reguard();
|
||||||
|
|
||||||
return to_route('broker.dashboard')->with('success', 'Deal has been created.');
|
return to_route('broker.deals.index')->with('success', 'Deal has been created.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,10 +91,10 @@ public function update(StoreBrokerDeal $request, Deal $deal, FileService $fileSe
|
|||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
Log::error($exception->getMessage(), $exception->getTrace());
|
Log::error($exception->getMessage(), $exception->getTrace());
|
||||||
|
|
||||||
return to_route('broker.dashboard')->with('error', 'Something gone wrong.');
|
return back()->with('error', 'Something gone wrong.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return to_route('broker.dashboard')->with('success', 'Deal has been updated.');
|
return back()->with('success', 'Deal has been updated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,9 +114,31 @@ public function destroy(Deal $deal, FileService $fileService)
|
|||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
Log::error($exception->getMessage(), $exception->getTrace());
|
Log::error($exception->getMessage(), $exception->getTrace());
|
||||||
|
|
||||||
return to_route('broker.dashboard')->with('error', 'Something gone wrong.');
|
return back()->with('error', 'Something gone wrong.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return to_route('broker.dashboard')->with('success', 'Deal has been deleted.');
|
return back()->with('success', 'Deal has been deleted.');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function deals()
|
||||||
|
{
|
||||||
|
return Auth::user()
|
||||||
|
->deals()
|
||||||
|
->select([
|
||||||
|
'id',
|
||||||
|
'title',
|
||||||
|
'description',
|
||||||
|
'image',
|
||||||
|
'active',
|
||||||
|
'slug',
|
||||||
|
'link',
|
||||||
|
'deal_category_id',
|
||||||
|
])
|
||||||
|
->with('category:id,name')
|
||||||
|
->WithLikePerDeal()
|
||||||
|
->WithRedirectionPerDeal()
|
||||||
|
->withViewPerDeal()
|
||||||
|
->latest()
|
||||||
|
->paginate(15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,16 +6,16 @@
|
|||||||
use App\Http\Requests\StoreBrokerProfileRequest;
|
use App\Http\Requests\StoreBrokerProfileRequest;
|
||||||
use App\Models\Broker;
|
use App\Models\Broker;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\ProfileInitialsService;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class BrokerProfileController extends Controller
|
class BrokerProfileController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*/
|
*/
|
||||||
public function show(User $profile)
|
public function show(User $profile, ProfileInitialsService $service)
|
||||||
{
|
{
|
||||||
// Get the broker profile
|
// Get the broker profile
|
||||||
$broker = $profile->type;
|
$broker = $profile->type;
|
||||||
@ -25,14 +25,7 @@ public function show(User $profile)
|
|||||||
abort(403, 'This user is not a broker.');
|
abort(403, 'This user is not a broker.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$initials = $service->create($profile->name);
|
||||||
* Create the initials from a full name (e.g. John Doe, Alex Mark, jane clerk)
|
|
||||||
* to display on profile page (e.g. JD, AM, JC).
|
|
||||||
*/
|
|
||||||
$initials = Str::of($profile->name)
|
|
||||||
->explode(' ')
|
|
||||||
->map(fn ($word) => Str::substr(ucfirst($word), 0, 1))
|
|
||||||
->join('');
|
|
||||||
|
|
||||||
return view('dashboards.broker.profile.show')
|
return view('dashboards.broker.profile.show')
|
||||||
->with('name', $profile->name)
|
->with('name', $profile->name)
|
||||||
|
|||||||
59
app/Http/Controllers/ChatController.php
Normal file
59
app/Http/Controllers/ChatController.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\CreateOrGetInboxAction;
|
||||||
|
use App\Actions\SendMessageAction;
|
||||||
|
use App\Exceptions\MessageNotSendException;
|
||||||
|
use App\Http\Requests\SendMessageRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Container\Attributes\CurrentUser;
|
||||||
|
use Log;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class ChatController extends Controller
|
||||||
|
{
|
||||||
|
//
|
||||||
|
public function index(#[CurrentUser] User $user)
|
||||||
|
{
|
||||||
|
return view('dashboards.user.chat')
|
||||||
|
->with('inboxes', $user->inboxes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(#[CurrentUser] User $sender, User $recipient, CreateOrGetInboxAction $action)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$inbox = $action->execute($recipient, $sender);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::error('Inbox instantiation Failed: ', [$e->getMessage()]);
|
||||||
|
abort(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('dashboards.user.chat')
|
||||||
|
->with('recipient', $recipient)
|
||||||
|
->with('inboxes', $sender->inboxes)
|
||||||
|
->with('messages', $inbox->messages()->latest()->get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function store(
|
||||||
|
#[CurrentUser] User $sender,
|
||||||
|
User $recipient,
|
||||||
|
SendMessageRequest $request,
|
||||||
|
SendMessageAction $action
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
$action->execute($sender, $recipient, $request->validated());
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Message sent successfully.']);
|
||||||
|
|
||||||
|
} catch (MessageNotSendException $e) {
|
||||||
|
|
||||||
|
Log::error('Message send failed', [$e->getMessage()]);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Message sent failed.'], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/Http/Controllers/CommentController.php
Normal file
44
app/Http/Controllers/CommentController.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\CommentRequest;
|
||||||
|
use App\Models\Comment;
|
||||||
|
use App\Models\Deal;
|
||||||
|
|
||||||
|
class CommentController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Deal $deal)
|
||||||
|
{
|
||||||
|
$comments = $deal->comments()->with('user')->latest()->get();
|
||||||
|
$html = view('components.dashboard.user.deal-comment.index', compact('comments'))->render();
|
||||||
|
|
||||||
|
return response()->json(['html' => $html]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Deal $deal, CommentRequest $request)
|
||||||
|
{
|
||||||
|
$data = $request->validated();
|
||||||
|
$data['user_id'] = $request->user()->id;
|
||||||
|
$data['deal_id'] = $deal->id;
|
||||||
|
|
||||||
|
Comment::create($data);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Comment created successfully.'], 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Comment $comment) {}
|
||||||
|
|
||||||
|
public function update(CommentRequest $request, Comment $comment)
|
||||||
|
{
|
||||||
|
$comment->update($request->validated());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Comment $comment)
|
||||||
|
{
|
||||||
|
$comment->delete();
|
||||||
|
|
||||||
|
return response()->json();
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/Http/Controllers/ContactController.php
Normal file
26
app/Http/Controllers/ContactController.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\ContactRequest;
|
||||||
|
use App\Models\Admin;
|
||||||
|
use App\Notifications\NewContactNotification;
|
||||||
|
|
||||||
|
class ContactController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(ContactRequest $request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
$data = $request->validated();
|
||||||
|
$admin = Admin::first();
|
||||||
|
$admin->user->notify(new NewContactNotification($data['name'], $data['email'], $data['message']));
|
||||||
|
|
||||||
|
return back()->with('success', 'Your message has been sent successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\Log::error('Error sending contact message', [$e->getMessage()]);
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,41 +2,60 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\AddRecentSearchAction;
|
||||||
|
use App\Enums\ExplorePageFilters;
|
||||||
use App\Enums\UserTypes;
|
use App\Enums\UserTypes;
|
||||||
|
use App\Http\Requests\ExploreSearchSortRequest;
|
||||||
use App\Models\Deal;
|
use App\Models\Deal;
|
||||||
|
use App\Models\DealCategory;
|
||||||
|
use App\Queries\ExplorePageDealsQuery;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class ExplorePageController extends Controller
|
class ExplorePageController extends Controller
|
||||||
{
|
{
|
||||||
public function __invoke()
|
public function __invoke(
|
||||||
{
|
ExploreSearchSortRequest $request,
|
||||||
|
ExplorePageDealsQuery $query,
|
||||||
|
AddRecentSearchAction $addRecentSearchAction
|
||||||
|
) {
|
||||||
return view('explore')
|
return view('explore')
|
||||||
->with('profileLink', $this->profileLink())
|
->with('profileLink', $this->profileLink())
|
||||||
->with('deals', $this->deals());
|
->with('categories', $this->categories())
|
||||||
|
->with('recentSearches', $this->recentSearches())
|
||||||
|
->with('deals', $this->deals($request, $query->builder(), $addRecentSearchAction));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function deals()
|
protected function deals(FormRequest $request, Builder $query, AddRecentSearchAction $action): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
return Deal::query()
|
$query->tap(fn ($q) => (new Deal)->withActiveDeals($q));
|
||||||
->select([
|
// Add a search query
|
||||||
'id', 'title', 'description', 'image', 'active', 'slug', 'link',
|
if ($request->has('search') && $request->get('search') !== null) {
|
||||||
'deal_category_id', 'user_id',
|
$query->tap(fn ($q) => (new Deal)->search($q, $request->search));
|
||||||
])
|
|
||||||
// Select additional details
|
\Illuminate\Support\defer(function () use ($action, $request) {
|
||||||
->with([
|
$action->execute($request->user(), ['query' => $request->search]);
|
||||||
'category:id,name',
|
});
|
||||||
'broker' => function ($query) {
|
}
|
||||||
$query->select('id', 'name', 'email', 'role_type', 'role_id')
|
|
||||||
->with('type');
|
// Add category sorting filter
|
||||||
},
|
if ($request->has('category') && $request->get('category') !== null) {
|
||||||
])
|
$query->tap(fn ($q) => (new Deal)->filterByCategory($q, $request->category));
|
||||||
// Select only admin-approved deals
|
}
|
||||||
->withActiveDeals()
|
|
||||||
// Check if the current user interacted with the deal
|
// Add sorting filters
|
||||||
->withCurrentUserInteractions()
|
$query = match (ExplorePageFilters::tryFrom($request->sortBy)) {
|
||||||
->withLikes()
|
ExplorePageFilters::Like => $query->orderBy('total_likes', 'desc'),
|
||||||
->latest()
|
ExplorePageFilters::Click => $query->orderBy('total_redirection', 'desc'),
|
||||||
->paginate();
|
default => $query->orderByRaw(
|
||||||
|
'((COALESCE(total_likes, 0) * 70.0) / 100.0) + ((COALESCE(total_redirection, 0) * 30.0) / 100.0) DESC'
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
return $query->latest()->paginate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,11 +64,28 @@ protected function deals()
|
|||||||
*
|
*
|
||||||
* @return string The URL for the user's dashboard.
|
* @return string The URL for the user's dashboard.
|
||||||
*/
|
*/
|
||||||
protected function profileLink()
|
protected function profileLink(): string
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
if ($user->role === UserTypes::Broker->value) {
|
|
||||||
return route('broker.profile.show', $user);
|
return match ($user->role ?? null) {
|
||||||
}
|
UserTypes::Broker->value => route('broker.profile.show', $user),
|
||||||
|
UserTypes::User->value => route('customer.profile.show', $user),
|
||||||
|
default => route('login.create')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function categories(): Collection
|
||||||
|
{
|
||||||
|
return DealCategory::all(['id', 'name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recentSearches(): Collection
|
||||||
|
{
|
||||||
|
if (! Auth::check()) {
|
||||||
|
return collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Auth::user()->recentSearches()->latest()->select(['id', 'query'])->get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
app/Http/Controllers/FollowController.php
Normal file
43
app/Http/Controllers/FollowController.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Broker;
|
||||||
|
use App\Models\Follow;
|
||||||
|
|
||||||
|
class FollowController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(Broker $broker)
|
||||||
|
{
|
||||||
|
$follow = $this->checkFollow($broker);
|
||||||
|
if ($follow === null) {
|
||||||
|
return $this->store($broker);
|
||||||
|
} else {
|
||||||
|
return $this->destroy($follow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Broker $broker)
|
||||||
|
{
|
||||||
|
Follow::create([
|
||||||
|
'broker_id' => $broker->id,
|
||||||
|
'customer_id' => auth()->id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Followed successfully.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Follow $follow)
|
||||||
|
{
|
||||||
|
$follow->delete();
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Unfollowed successfully.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkFollow(Broker $broker)
|
||||||
|
{
|
||||||
|
return Follow::where('broker_id', $broker->id)
|
||||||
|
->where('customer_id', auth()->id())
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
136
app/Http/Controllers/Interaction/InteractionController.php
Normal file
136
app/Http/Controllers/Interaction/InteractionController.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Interaction;
|
||||||
|
|
||||||
|
use App\Enums\InteractionType;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Deal;
|
||||||
|
use App\Models\Interaction;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class InteractionController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Interact to a deal by Like or Favorite state
|
||||||
|
*
|
||||||
|
* @param InteractionType $type [InteractionType::Like, InteractionType::Favorite]
|
||||||
|
*/
|
||||||
|
public function togglesState(Deal $deal, InteractionType $type, Request $request)
|
||||||
|
{
|
||||||
|
if (! in_array($type, [InteractionType::Like, InteractionType::Favorite])) {
|
||||||
|
return response()->json(['error' => 'This interaction is not supported'], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check for existing like of the user with deal
|
||||||
|
$existingInteraction = $deal->interactions()
|
||||||
|
->where('user_id', Auth::id())
|
||||||
|
->where('type', $type)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
// Delete the existing like if exists, else add the like
|
||||||
|
$message = '';
|
||||||
|
if ($existingInteraction) {
|
||||||
|
$existingInteraction->delete();
|
||||||
|
$message = ucfirst($type->value).' removed from deal';
|
||||||
|
} else {
|
||||||
|
$data = [
|
||||||
|
'type' => $type,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Interaction::unguard();
|
||||||
|
$deal->interactions()->create($data);
|
||||||
|
Interaction::reguard();
|
||||||
|
|
||||||
|
$message = ucfirst($type->value).' added to deal';
|
||||||
|
}
|
||||||
|
if ($request->expectsJson()) {
|
||||||
|
return response()->json(['message' => $message]);
|
||||||
|
} else {
|
||||||
|
return back()->with('success', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error when liked a deal',
|
||||||
|
[
|
||||||
|
'deal_id' => $deal->id,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'type' => $type,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
if ($request->expectsJson()) {
|
||||||
|
return response()->json(['error' => 'Something went wrong.'], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function redirect(Deal $deal)
|
||||||
|
{
|
||||||
|
if (blank($deal->link)) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Illuminate\Support\defer(function () use ($deal) {
|
||||||
|
try {
|
||||||
|
$interaction = $deal->interactions()->firstOrCreate([
|
||||||
|
'type' => InteractionType::Redirection,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $interaction->wasRecentlyCreated) {
|
||||||
|
$interaction->increment('count');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error when redirecting a deal external link',
|
||||||
|
[
|
||||||
|
'deal_id' => $deal->id,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
abort(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->away($deal->link);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(Deal $deal)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$interaction = $deal->interactions()->firstOrCreate([
|
||||||
|
'type' => InteractionType::View,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (! $interaction->wasRecentlyCreated) {
|
||||||
|
$interaction->increment('count');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error when view a deal',
|
||||||
|
[
|
||||||
|
'deal_id' => $deal->id,
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json(['error' => 'Something went wrong.'], 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'View counted',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers\Interaction;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\StoreReportRequest;
|
use App\Http\Requests\StoreReportRequest;
|
||||||
use App\Models\Deal;
|
use App\Models\Deal;
|
||||||
use App\Models\Report;
|
use App\Models\Report;
|
||||||
@ -29,9 +30,9 @@ public function store(StoreReportRequest $request, Deal $deal)
|
|||||||
$data['user_id'] = Auth::id();
|
$data['user_id'] = Auth::id();
|
||||||
|
|
||||||
// Check if the user already reported the deal
|
// Check if the user already reported the deal
|
||||||
$alreadyReported = $deal->reports()->where('user_id', Auth::id())->first();
|
$alreadyReported = $deal->reports()->where('user_id', Auth::id())->exists();
|
||||||
if ($alreadyReported) {
|
if ($alreadyReported) {
|
||||||
return response()->json(['message' => 'You already reported this report'], 405);
|
return response()->json(['message' => 'You had already reported this deal'], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -41,7 +42,7 @@ public function store(StoreReportRequest $request, Deal $deal)
|
|||||||
Report::reguard();
|
Report::reguard();
|
||||||
});
|
});
|
||||||
|
|
||||||
return response()->json(['message' => 'Report created'], 201);
|
return response()->json(['message' => 'Report submitted. Thank you for keeping DealHub safe'], 201);
|
||||||
|
|
||||||
} catch (\Throwable $exception) {
|
} catch (\Throwable $exception) {
|
||||||
Log::error('Error creating report', [
|
Log::error('Error creating report', [
|
||||||
@ -77,6 +78,21 @@ public function update(Request $request, Report $report)
|
|||||||
*/
|
*/
|
||||||
public function destroy(Report $report)
|
public function destroy(Report $report)
|
||||||
{
|
{
|
||||||
//
|
try {
|
||||||
|
DB::transaction(function () use ($report) {
|
||||||
|
$report->deals()->detach();
|
||||||
|
$report->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('success', 'Report deleted successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Error deleting report', [
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'report_id' => $report->id,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,66 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Enums\InteractionType;
|
|
||||||
use App\Models\Deal;
|
|
||||||
use App\Models\Interaction;
|
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class InteractionController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Interact to a deal by Like or Favorite state
|
|
||||||
*
|
|
||||||
* @param InteractionType $type [InteractionType::Like, InteractionType::Favorite]
|
|
||||||
* @return \Illuminate\Http\JsonResponse
|
|
||||||
*/
|
|
||||||
public function togglesState(Deal $deal, InteractionType $type)
|
|
||||||
{
|
|
||||||
if (! in_array($type, [InteractionType::Like, InteractionType::Favorite])) {
|
|
||||||
return response()->json(['error' => 'This interaction is not supported'], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check for existing like of the user with deal
|
|
||||||
$existingInteraction = $deal->interactions()
|
|
||||||
->where('user_id', Auth::id())
|
|
||||||
->where('type', $type)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
// Delete the existing like if exists, else add the like
|
|
||||||
$message = '';
|
|
||||||
if ($existingInteraction) {
|
|
||||||
$existingInteraction->delete();
|
|
||||||
$message = "{$type->value} removed from deal";
|
|
||||||
} else {
|
|
||||||
$data = [
|
|
||||||
'type' => $type,
|
|
||||||
'user_id' => Auth::id(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Interaction::unguard();
|
|
||||||
$deal->interactions()->create($data);
|
|
||||||
Interaction::reguard();
|
|
||||||
|
|
||||||
$message = "{$type->value} added to deal";
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['message' => $message]);
|
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Error when liked a deal',
|
|
||||||
[
|
|
||||||
'deal_id' => $deal->id,
|
|
||||||
'use_id' => Auth::id(),
|
|
||||||
'type' => $type,
|
|
||||||
'error' => $e->getMessage(),
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
return response()->json(['error' => 'Something went wrong.'], 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
app/Http/Controllers/PushSubscriptionController.php
Normal file
19
app/Http/Controllers/PushSubscriptionController.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\PushSubscriptionRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Container\Attributes\CurrentUser;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
class PushSubscriptionController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(#[CurrentUser] User $user, PushSubscriptionRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
$data = $request->validated();
|
||||||
|
$user->updatePushSubscription($data['endpoint'], $data['keys']['p256dh'], $data['keys']['auth']);
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Push subscription updated successfully.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/Http/Controllers/RecentSearchController.php
Normal file
15
app/Http/Controllers/RecentSearchController.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\RecentSearch;
|
||||||
|
|
||||||
|
class RecentSearchController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(RecentSearch $recentSearch)
|
||||||
|
{
|
||||||
|
$recentSearch->delete();
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Search deleted successfully.']);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/Http/Controllers/StatsController.php
Normal file
36
app/Http/Controllers/StatsController.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Enums\UserTypes;
|
||||||
|
use App\Http\Resources\ActiveUsersStatsCollection;
|
||||||
|
use App\Http\Resources\DealsCountByCategoryCollection;
|
||||||
|
use App\Models\DealCategory;
|
||||||
|
use App\Queries\PageVisitStatsQuery;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class StatsController extends Controller
|
||||||
|
{
|
||||||
|
public function getActiveUsers(Request $request, PageVisitStatsQuery $baseQuery)
|
||||||
|
{
|
||||||
|
$startDay = $request->from ?? now()->subDays(30);
|
||||||
|
$endDay = $request->to ?? now();
|
||||||
|
|
||||||
|
$activeCustomers = $baseQuery->builder(UserTypes::User, $startDay, $endDay)->get();
|
||||||
|
$activeBrokers = $baseQuery->builder(UserTypes::Broker, $startDay, $endDay)->get();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'activeCustomers' => new ActiveUsersStatsCollection($activeCustomers),
|
||||||
|
'activeBrokers' => new ActiveUsersStatsCollection($activeBrokers),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDealsByCategory()
|
||||||
|
{
|
||||||
|
return new DealsCountByCategoryCollection(
|
||||||
|
DealCategory::select(['id', 'name'])
|
||||||
|
->withCount('deals')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
app/Http/Controllers/User/UserProfileController.php
Normal file
73
app/Http/Controllers/User/UserProfileController.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\User;
|
||||||
|
|
||||||
|
use App\Actions\GetUserFavoritesAction;
|
||||||
|
use App\Actions\GetUserReportedDealsAction;
|
||||||
|
use App\Actions\UpdateCustomerAction;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\StoreCustomerProfileRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\ProfileInitialsService;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class UserProfileController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(
|
||||||
|
User $profile,
|
||||||
|
ProfileInitialsService $service,
|
||||||
|
GetUserFavoritesAction $favoritesAction,
|
||||||
|
GetUserReportedDealsAction $reportedDealsAction
|
||||||
|
) {
|
||||||
|
// Get the user profile
|
||||||
|
$user = $profile->type;
|
||||||
|
|
||||||
|
$initials = $service->create($profile->name);
|
||||||
|
|
||||||
|
return view('dashboards.user.profile.show')
|
||||||
|
->with('user', $profile)
|
||||||
|
->with('name', $profile->name)
|
||||||
|
->with('joinDate', $profile->created_at->format('F Y'))
|
||||||
|
->with('email', $profile->email)
|
||||||
|
->with('initials', $initials)
|
||||||
|
->with('location', $user->location)
|
||||||
|
->with('bio', $user->bio)
|
||||||
|
->with('phone', $user->phone)
|
||||||
|
->with('favorites', $favoritesAction->execute($profile))
|
||||||
|
->with('reported', $reportedDealsAction->execute($profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(User $profile)
|
||||||
|
{
|
||||||
|
return view('dashboards.user.profile.edit')
|
||||||
|
->with('profile', $profile)
|
||||||
|
->with('pageTitle', 'Edit Profile')
|
||||||
|
->with('title', 'Edit Your Profile')
|
||||||
|
->with('description', 'Update your profile information.')
|
||||||
|
->with('backLink', route('customer.profile.show', $profile))
|
||||||
|
->with('actionLink', route('customer.profile.update', $profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(StoreCustomerProfileRequest $request, User $profile, UpdateCustomerAction $action)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$action->execute($request->validated(), $profile);
|
||||||
|
|
||||||
|
return to_route('customer.profile.show', $profile)
|
||||||
|
->with('success', 'Profile updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Customer Profile Update Failed: '.$e->getMessage(), $e->getTrace());
|
||||||
|
|
||||||
|
return back()->withInput()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Http/Middleware/EnsureUserFollowedBroker.php
Normal file
38
app/Http/Middleware/EnsureUserFollowedBroker.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class EnsureUserFollowedBroker
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
$sender = \Auth::user();
|
||||||
|
if ($sender->isBroker()) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipientUser = $request->route('recipient');
|
||||||
|
if ($recipientUser->isBroker()) {
|
||||||
|
$recipient = $recipientUser->type;
|
||||||
|
|
||||||
|
$isFollowing = $recipient->followers->contains($sender->id);
|
||||||
|
|
||||||
|
if ($isFollowing) {
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(403, 'You are not following this broker.');
|
||||||
|
}
|
||||||
|
|
||||||
|
abort('404', 'Broker not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Http/Requests/CommentRequest.php
Normal file
20
app/Http/Requests/CommentRequest.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class CommentRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'text' => ['required', 'string', 'max:255'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Http/Requests/ContactRequest.php
Normal file
35
app/Http/Requests/ContactRequest.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class ContactRequest 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 [
|
||||||
|
'name' => 'required|string|min:3|max:255',
|
||||||
|
'email' => 'required|email|max:255',
|
||||||
|
'message' => 'required|string|min:10|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRedirectUrl(): string
|
||||||
|
{
|
||||||
|
return parent::getRedirectUrl().'#contact';
|
||||||
|
}
|
||||||
|
}
|
||||||
28
app/Http/Requests/DealOutboundRequest.php
Normal file
28
app/Http/Requests/DealOutboundRequest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class DealOutboundRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
32
app/Http/Requests/ExploreSearchSortRequest.php
Normal file
32
app/Http/Requests/ExploreSearchSortRequest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use App\Enums\ExplorePageFilters;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class ExploreSearchSortRequest 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 [
|
||||||
|
'sortBy' => ['nullable', 'string', Rule::in(ExplorePageFilters::values())],
|
||||||
|
'search' => ['nullable', 'string', 'min:1', 'max:255'],
|
||||||
|
'category' => ['nullable', 'exists:deal_categories,id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Http/Requests/PushSubscriptionRequest.php
Normal file
30
app/Http/Requests/PushSubscriptionRequest.php
Normal 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:500',
|
||||||
|
'keys.auth' => 'required|string|max:255',
|
||||||
|
'keys.p256dh' => 'required|string|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Http/Requests/SendMessageRequest.php
Normal file
20
app/Http/Requests/SendMessageRequest.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class SendMessageRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use AllowDynamicProperties;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
#[AllowDynamicProperties]
|
||||||
class StoreBrokerProfileRequest extends FormRequest
|
class StoreBrokerProfileRequest extends FormRequest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -12,7 +14,17 @@ class StoreBrokerProfileRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return $this->user()->isBroker();
|
// If this request is by a broker profile, then only allow the owner to update it.
|
||||||
|
if (isset($this->profile)) {
|
||||||
|
$this->user = $this->profile;
|
||||||
|
|
||||||
|
return $this->user()->id === $this->profile->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this request is by an admin, then allow them to update any profile.
|
||||||
|
$this->user = $this->broker->user;
|
||||||
|
|
||||||
|
return $this->user()->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +37,7 @@ public function rules(): array
|
|||||||
return [
|
return [
|
||||||
'name' => 'required|string|min:3|max:255',
|
'name' => 'required|string|min:3|max:255',
|
||||||
'bio' => 'required|string|min:10|max:255',
|
'bio' => 'required|string|min:10|max:255',
|
||||||
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->user()->id)],
|
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->user->id)],
|
||||||
'phone' => 'required|string|min:10|max:255',
|
'phone' => 'required|string|min:10|max:255',
|
||||||
'location' => 'required|string|min:3|max:255',
|
'location' => 'required|string|min:3|max:255',
|
||||||
];
|
];
|
||||||
|
|||||||
45
app/Http/Requests/StoreCustomerProfileRequest.php
Normal file
45
app/Http/Requests/StoreCustomerProfileRequest.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use AllowDynamicProperties;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
#[AllowDynamicProperties]
|
||||||
|
class StoreCustomerProfileRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
// If this request is by a customer profile, then only allow the owner to update it.
|
||||||
|
if (isset($this->profile)) {
|
||||||
|
$this->user = $this->profile;
|
||||||
|
|
||||||
|
return $this->user()->id === $this->profile->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this request is by an admin, then allow them to update any profile.
|
||||||
|
$this->user = $this->customer->user;
|
||||||
|
|
||||||
|
return $this->user()->isAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|string|min:3|max:255',
|
||||||
|
'bio' => 'required|string|min:10|max:255',
|
||||||
|
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->user->id)],
|
||||||
|
'phone' => 'required|string|min:10|max:255',
|
||||||
|
'location' => 'required|string|min:3|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Http/Resources/ActiveUsersStatsCollection.php
Normal file
16
app/Http/Resources/ActiveUsersStatsCollection.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||||
|
|
||||||
|
class ActiveUsersStatsCollection extends ResourceCollection
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => $this->collection,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Http/Resources/ActiveUsersStatsResource.php
Normal file
20
app/Http/Resources/ActiveUsersStatsResource.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class ActiveUsersStatsResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array{data: string, userCount: int}
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'date' => $this->date,
|
||||||
|
'userCount' => (int) $this->user_count,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Http/Resources/BrokerRoleResource.php
Normal file
23
app/Http/Resources/BrokerRoleResource.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class BrokerRoleResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'bio' => $this->bio,
|
||||||
|
'location' => $this->location,
|
||||||
|
'phone' => $this->phone,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/Http/Resources/DealResource.php
Normal file
33
app/Http/Resources/DealResource.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
use Illuminate\Support\Facades\URL;
|
||||||
|
|
||||||
|
class DealResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'title' => $this->title,
|
||||||
|
'description' => $this->description,
|
||||||
|
'image' => asset('storage/'.$this->image),
|
||||||
|
'link' => $this->link !== null ? URL::signedRoute('redirect', $this->id) : null,
|
||||||
|
'category' => $this->whenLoaded('category'),
|
||||||
|
'broker' => new UserResource($this->whenLoaded('broker')),
|
||||||
|
'totalLikes' => $this->total_likes,
|
||||||
|
'totalRedirection' => $this->total_redirection,
|
||||||
|
'isLiked' => $this->is_liked,
|
||||||
|
'isFavorite' => $this->is_favorite,
|
||||||
|
'isFollowed' => $this->is_followed,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/Http/Resources/DealsCountByCategoryCollection.php
Normal file
16
app/Http/Resources/DealsCountByCategoryCollection.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||||
|
|
||||||
|
class DealsCountByCategoryCollection extends ResourceCollection
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'data' => $this->collection,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Http/Resources/DealsCountByCategoryResource.php
Normal file
17
app/Http/Resources/DealsCountByCategoryResource.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class DealsCountByCategoryResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->name,
|
||||||
|
'dealsCount' => $this->deals_count,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/Http/Resources/UserResource.php
Normal file
24
app/Http/Resources/UserResource.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class UserResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'email' => $this->email,
|
||||||
|
'role' => new BrokerRoleResource($this->whenLoaded('role')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
38
app/Mail/PasswordResetMail.php
Normal file
38
app/Mail/PasswordResetMail.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Mail\Mailables\Address;
|
||||||
|
use Illuminate\Mail\Mailables\Content;
|
||||||
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class PasswordResetMail extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(private readonly string $otp) {}
|
||||||
|
|
||||||
|
public function envelope(): Envelope
|
||||||
|
{
|
||||||
|
return new Envelope(
|
||||||
|
from: new Address(config('mail.from.address'), config('mail.from.name')),
|
||||||
|
subject: 'Password Reset',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content(): Content
|
||||||
|
{
|
||||||
|
return new Content(
|
||||||
|
markdown: 'emails.password-reset',
|
||||||
|
with: ['otp' => $this->otp]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachments(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Models/Admin.php
Normal file
29
app/Models/Admin.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Admin whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class Admin extends Model
|
||||||
|
{
|
||||||
|
public function user(): MorphOne
|
||||||
|
{
|
||||||
|
return $this->morphOne(User::class, 'role');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,10 +3,39 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string|null $bio
|
||||||
|
* @property string|null $location
|
||||||
|
* @property string|null $phone
|
||||||
|
* @property bool $verified
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\Follow|null $pivot
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Customer> $followers
|
||||||
|
* @property-read int|null $followers_count
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereBio($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereLocation($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker wherePhone($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereUpdatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Broker whereVerified($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class Broker extends Model
|
class Broker extends Model
|
||||||
{
|
{
|
||||||
|
protected $fillable = ['bio', 'location', 'phone'];
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -18,4 +47,14 @@ public function user(): MorphOne
|
|||||||
{
|
{
|
||||||
return $this->morphOne(User::class, 'role');
|
return $this->morphOne(User::class, 'role');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function followers(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(
|
||||||
|
Customer::class,
|
||||||
|
'follows',
|
||||||
|
'broker_id',
|
||||||
|
'customer_id'
|
||||||
|
)->using(Follow::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
app/Models/Comment.php
Normal file
47
app/Models/Comment.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $text
|
||||||
|
* @property int $deal_id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\Deal|null $deal
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereDealId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereText($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereUpdatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Comment whereUserId($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class Comment extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'text',
|
||||||
|
'deal_id',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function deal(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Deal::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
app/Models/Customer.php
Normal file
45
app/Models/Customer.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string|null $bio
|
||||||
|
* @property string|null $location
|
||||||
|
* @property string|null $phone
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Follow> $followings
|
||||||
|
* @property-read int|null $followings_count
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereBio($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereLocation($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer wherePhone($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Customer whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class Customer extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['bio', 'location', 'phone'];
|
||||||
|
|
||||||
|
public function user(): MorphOne
|
||||||
|
{
|
||||||
|
return $this->morphOne(User::class, 'role');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function followings(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Follow::class, 'customer_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\InteractionType;
|
use App\Enums\InteractionType;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
@ -10,6 +11,52 @@
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $title
|
||||||
|
* @property string $slug
|
||||||
|
* @property string $description
|
||||||
|
* @property string|null $image
|
||||||
|
* @property string|null $link
|
||||||
|
* @property int $active
|
||||||
|
* @property int $deal_category_id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\User|null $broker
|
||||||
|
* @property-read \App\Models\DealCategory|null $category
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Comment> $comments
|
||||||
|
* @property-read int|null $comments_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Interaction> $interactions
|
||||||
|
* @property-read int|null $interactions_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Report> $reports
|
||||||
|
* @property-read int|null $reports_count
|
||||||
|
*
|
||||||
|
* @method static Builder<static>|Deal WithActiveDeals()
|
||||||
|
* @method static Builder<static>|Deal WithCurrentUserInteractions()
|
||||||
|
* @method static Builder<static>|Deal WithLikePerDeal()
|
||||||
|
* @method static Builder<static>|Deal WithRedirectionPerDeal()
|
||||||
|
* @method static Builder<static>|Deal filterByCategory(string $category)
|
||||||
|
* @method static Builder<static>|Deal newModelQuery()
|
||||||
|
* @method static Builder<static>|Deal newQuery()
|
||||||
|
* @method static Builder<static>|Deal query()
|
||||||
|
* @method static Builder<static>|Deal search(string $search)
|
||||||
|
* @method static Builder<static>|Deal whereActive($value)
|
||||||
|
* @method static Builder<static>|Deal whereCreatedAt($value)
|
||||||
|
* @method static Builder<static>|Deal whereDealCategoryId($value)
|
||||||
|
* @method static Builder<static>|Deal whereDescription($value)
|
||||||
|
* @method static Builder<static>|Deal whereId($value)
|
||||||
|
* @method static Builder<static>|Deal whereImage($value)
|
||||||
|
* @method static Builder<static>|Deal whereLink($value)
|
||||||
|
* @method static Builder<static>|Deal whereSlug($value)
|
||||||
|
* @method static Builder<static>|Deal whereTitle($value)
|
||||||
|
* @method static Builder<static>|Deal whereUpdatedAt($value)
|
||||||
|
* @method static Builder<static>|Deal whereUserId($value)
|
||||||
|
* @method static Builder<static>|Deal withIsFollowedByCurrentUser()
|
||||||
|
* @method static Builder<static>|Deal withViewPerDeal()
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class Deal extends Model
|
class Deal extends Model
|
||||||
{
|
{
|
||||||
public function broker(): BelongsTo
|
public function broker(): BelongsTo
|
||||||
@ -27,18 +74,30 @@ public function interactions(): HasMany
|
|||||||
return $this->hasMany(Interaction::class);
|
return $this->hasMany(Interaction::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function reports(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Report::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function comments(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Comment::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get deals that are active
|
* Scope a query to only include active deals
|
||||||
*/
|
*/
|
||||||
public function scopeWithActiveDeals(Builder $query): Builder
|
#[Scope]
|
||||||
|
public function WithActiveDeals(Builder $query): Builder
|
||||||
{
|
{
|
||||||
return $query->where('active', true);
|
return $query->where('active', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get if the current user has liked or favorite the deal
|
* Scope a query to determine if the current user has liked and favorite a deal
|
||||||
*/
|
*/
|
||||||
public function scopeWithCurrentUserInteractions(Builder $query): Builder
|
#[Scope]
|
||||||
|
public function WithCurrentUserInteractions(Builder $query): Builder
|
||||||
{
|
{
|
||||||
return $query->withExists([
|
return $query->withExists([
|
||||||
'interactions as is_liked' => function ($query) {
|
'interactions as is_liked' => function ($query) {
|
||||||
@ -52,7 +111,11 @@ public function scopeWithCurrentUserInteractions(Builder $query): Builder
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function scopeWithLikes(Builder $query): Builder
|
/**
|
||||||
|
* Scope a query to get total like count per deal
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function WithLikePerDeal(Builder $query): Builder
|
||||||
{
|
{
|
||||||
return $query->withCount([
|
return $query->withCount([
|
||||||
'interactions as total_likes' => function ($query) {
|
'interactions as total_likes' => function ($query) {
|
||||||
@ -61,8 +124,76 @@ public function scopeWithLikes(Builder $query): Builder
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reports(): BelongsToMany
|
/**
|
||||||
|
* Scope a query to get click count per deal
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function WithRedirectionPerDeal(Builder $query): Builder
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Report::class);
|
return $query->withSum([
|
||||||
|
'interactions as total_redirection' => function ($query) {
|
||||||
|
$query->where('type', InteractionType::Redirection);
|
||||||
|
},
|
||||||
|
], 'count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope a query to get a view count per deal
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function withViewPerDeal(Builder $query): Builder
|
||||||
|
{
|
||||||
|
return $query->withSum([
|
||||||
|
'interactions as total_views' => function ($query) {
|
||||||
|
$query->where('type', InteractionType::View);
|
||||||
|
},
|
||||||
|
], 'count');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope a search in a query
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function search(Builder $query, string $search): Builder
|
||||||
|
{
|
||||||
|
return $query->where(function (Builder $query) use ($search) {
|
||||||
|
$query->where('title', 'LIKE', "%$search%")
|
||||||
|
->orWhereRelation('broker', 'name', 'LIKE', "%$search%");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope a category filter in a query
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function filterByCategory(Builder $query, string $category): Builder
|
||||||
|
{
|
||||||
|
return $query->where('deal_category_id', $category);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add this to App\Models\Deal.php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope a query to check if the current user follows the deal's broker
|
||||||
|
*/
|
||||||
|
#[Scope]
|
||||||
|
public function withIsFollowedByCurrentUser(Builder $query): Builder
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
if (! $user || $user->role_type !== \App\Models\Customer::class) {
|
||||||
|
return $query->withExists(['broker as is_followed' => fn ($q) => $q->whereRaw('1 = 0')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->withExists([
|
||||||
|
'broker as is_followed' => function ($query) use ($user) {
|
||||||
|
$query->where('role_type', \App\Models\Broker::class)
|
||||||
|
->whereHasMorph('type', [\App\Models\Broker::class], function ($query) use ($user) {
|
||||||
|
$query->whereHas('followers', function ($query) use ($user) {
|
||||||
|
$query->where('customer_id', $user->id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,36 @@
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property string|null $description
|
||||||
|
* @property string $slug
|
||||||
|
* @property int $active
|
||||||
|
* @property int $order
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Deal> $deals
|
||||||
|
* @property-read int|null $deals_count
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereActive($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereDescription($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereName($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereOrder($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereSlug($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|DealCategory whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class DealCategory extends Model
|
class DealCategory extends Model
|
||||||
{
|
{
|
||||||
|
protected $fillable = ['name', 'description', 'slug', 'active', 'order'];
|
||||||
|
|
||||||
public function deals(): HasMany
|
public function deals(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Deal::class);
|
return $this->hasMany(Deal::class);
|
||||||
|
|||||||
46
app/Models/Follow.php
Normal file
46
app/Models/Follow.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $customer_id
|
||||||
|
* @property int $broker_id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\Broker|null $broker
|
||||||
|
* @property-read \App\Models\Customer|null $customer
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow whereBrokerId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow whereCustomerId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Follow whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class Follow extends Pivot
|
||||||
|
{
|
||||||
|
protected $table = 'follows';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'customer_id',
|
||||||
|
'broker_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function customer(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Customer::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function broker(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Broker::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
app/Models/Inbox.php
Normal file
55
app/Models/Inbox.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string|null $last_message
|
||||||
|
* @property int|null $last_user_id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\User|null $lastUser
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\User> $users
|
||||||
|
* @property-read int|null $users_count
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox whereLastMessage($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox whereLastUserId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Inbox whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class Inbox extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['last_user_id', 'last_message'];
|
||||||
|
|
||||||
|
public function users(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lastUser(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'id', 'last_user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Message::class, 'inbox_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecipientAttribute(): User
|
||||||
|
{
|
||||||
|
// first user in the relationship that is NOT the authenticated user
|
||||||
|
return $this->users->where('id', '!=', auth()->id())->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,37 @@
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property int $deal_id
|
||||||
|
* @property InteractionType $type
|
||||||
|
* @property int $count
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\Deal|null $deal
|
||||||
|
* @property-read \App\Models\Deal|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereCount($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereDealId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereType($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereUpdatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|Interaction whereUserId($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class Interaction extends Model
|
class Interaction extends Model
|
||||||
{
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'type',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
public function user(): BelongsTo
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Deal::class);
|
return $this->belongsTo(Deal::class);
|
||||||
|
|||||||
62
app/Models/Message.php
Normal file
62
app/Models/Message.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Eloquent;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property int $inbox_id
|
||||||
|
* @property string $message
|
||||||
|
* @property string|null $read_at
|
||||||
|
* @property string|null $delivered_at
|
||||||
|
* @property string|null $deleted_at
|
||||||
|
* @property string|null $failed_at
|
||||||
|
* @property Carbon $created_at
|
||||||
|
* @property-read Inbox|null $inbox
|
||||||
|
* @property-read User|null $user
|
||||||
|
*
|
||||||
|
* @method static Builder<static>|Message newModelQuery()
|
||||||
|
* @method static Builder<static>|Message newQuery()
|
||||||
|
* @method static Builder<static>|Message query()
|
||||||
|
* @method static Builder<static>|Message whereCreatedAt($value)
|
||||||
|
* @method static Builder<static>|Message whereDeletedAt($value)
|
||||||
|
* @method static Builder<static>|Message whereDeliveredAt($value)
|
||||||
|
* @method static Builder<static>|Message whereFailedAt($value)
|
||||||
|
* @method static Builder<static>|Message whereId($value)
|
||||||
|
* @method static Builder<static>|Message whereInboxId($value)
|
||||||
|
* @method static Builder<static>|Message whereMessage($value)
|
||||||
|
* @method static Builder<static>|Message whereReadAt($value)
|
||||||
|
* @method static Builder<static>|Message whereUserId($value)
|
||||||
|
*
|
||||||
|
* @mixin Eloquent
|
||||||
|
*/
|
||||||
|
class Message extends Model
|
||||||
|
{
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'inbox_id', 'user_id', 'message',
|
||||||
|
'read_at', 'deleted_at', 'failed_at',
|
||||||
|
'created_at', 'delivered_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function inbox(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Inbox::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Models/PageVisit.php
Normal file
41
app/Models/PageVisit.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\UserTypes;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int|null $user_id
|
||||||
|
* @property string $page
|
||||||
|
* @property UserTypes|null $user_type
|
||||||
|
* @property string $created_at
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit wherePage($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit whereUserId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|PageVisit whereUserType($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class PageVisit extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id', 'page', 'user_type',
|
||||||
|
'created_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user_type' => UserTypes::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Models/RecentSearch.php
Normal file
35
app/Models/RecentSearch.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $query
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch whereQuery($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch whereUpdatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|RecentSearch whereUserId($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
|
class RecentSearch extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['query'];
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,10 +4,38 @@
|
|||||||
|
|
||||||
use App\Enums\ReportStatus;
|
use App\Enums\ReportStatus;
|
||||||
use App\Enums\ReportType;
|
use App\Enums\ReportType;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
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\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property ReportType $type
|
||||||
|
* @property ReportStatus $status
|
||||||
|
* @property string $description
|
||||||
|
* @property int $user_id
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Deal> $deals
|
||||||
|
* @property-read int|null $deals_count
|
||||||
|
* @property-read \App\Models\User|null $user
|
||||||
|
*
|
||||||
|
* @method static Builder<static>|Report newModelQuery()
|
||||||
|
* @method static Builder<static>|Report newQuery()
|
||||||
|
* @method static Builder<static>|Report orderByStatus(array $statusOrder)
|
||||||
|
* @method static Builder<static>|Report query()
|
||||||
|
* @method static Builder<static>|Report whereCreatedAt($value)
|
||||||
|
* @method static Builder<static>|Report whereDescription($value)
|
||||||
|
* @method static Builder<static>|Report whereId($value)
|
||||||
|
* @method static Builder<static>|Report whereStatus($value)
|
||||||
|
* @method static Builder<static>|Report whereType($value)
|
||||||
|
* @method static Builder<static>|Report whereUpdatedAt($value)
|
||||||
|
* @method static Builder<static>|Report whereUserId($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class Report extends Model
|
class Report extends Model
|
||||||
{
|
{
|
||||||
public function user(): BelongsTo
|
public function user(): BelongsTo
|
||||||
@ -27,4 +55,15 @@ protected function casts(): array
|
|||||||
'status' => ReportStatus::class,
|
'status' => ReportStatus::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Scope]
|
||||||
|
public function orderByStatus(Builder $query, array $statusOrder): Builder
|
||||||
|
{
|
||||||
|
$values = array_map(fn ($enum) => $enum->value, $statusOrder);
|
||||||
|
|
||||||
|
// Create placeholders for each value: FIELD(status, ?, ?, ?)
|
||||||
|
$placeholders = implode(',', array_fill(0, count($values), '?'));
|
||||||
|
|
||||||
|
return $query->orderByRaw("FIELD(status, $placeholders)", $values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,15 +5,74 @@
|
|||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
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 string $name
|
||||||
|
* @property string $email
|
||||||
|
* @property \Illuminate\Support\Carbon|null $email_verified_at
|
||||||
|
* @property string $password
|
||||||
|
* @property string|null $remember_token
|
||||||
|
* @property \Illuminate\Support\Carbon|null $created_at
|
||||||
|
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||||
|
* @property string $status
|
||||||
|
* @property string $role
|
||||||
|
* @property string|null $role_type
|
||||||
|
* @property int|null $role_id
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Comment> $comments
|
||||||
|
* @property-read int|null $comments_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Deal> $deals
|
||||||
|
* @property-read int|null $deals_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Interaction> $dealsInteractions
|
||||||
|
* @property-read int|null $deals_interactions_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Inbox> $inboxes
|
||||||
|
* @property-read int|null $inboxes_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Deal> $interactedDeals
|
||||||
|
* @property-read int|null $interacted_deals_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, User> $interactions
|
||||||
|
* @property-read int|null $interactions_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Message> $messages
|
||||||
|
* @property-read int|null $messages_count
|
||||||
|
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
|
||||||
|
* @property-read int|null $notifications_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \NotificationChannels\WebPush\PushSubscription> $pushSubscriptions
|
||||||
|
* @property-read int|null $push_subscriptions_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\RecentSearch> $recentSearches
|
||||||
|
* @property-read int|null $recent_searches_count
|
||||||
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Report> $reports
|
||||||
|
* @property-read int|null $reports_count
|
||||||
|
* @property-read Model|\Eloquent|null $type
|
||||||
|
*
|
||||||
|
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRole($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRoleId($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRoleType($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereStatus($value)
|
||||||
|
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
|
||||||
|
*
|
||||||
|
* @mixin \Eloquent
|
||||||
|
*/
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||||
use HasFactory, Notifiable;
|
use HasFactory, HasPushSubscriptions, Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
@ -56,6 +115,16 @@ public function isBroker(): bool
|
|||||||
return $this->type instanceof Broker;
|
return $this->type instanceof Broker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isAdmin(): bool
|
||||||
|
{
|
||||||
|
return $this->type instanceof Admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCustomer(): bool
|
||||||
|
{
|
||||||
|
return $this->type instanceof Customer;
|
||||||
|
}
|
||||||
|
|
||||||
public function deals(): HasMany
|
public function deals(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Deal::class);
|
return $this->hasMany(Deal::class);
|
||||||
@ -73,4 +142,39 @@ public function interactions(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(User::class);
|
return $this->hasMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function recentSearches(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(RecentSearch::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dealsInteractions(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Interaction::class, Deal::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function interactedDeals(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(Deal::class, Interaction::class, 'user_id', 'id', 'id', 'deal_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reports(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Report::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function comments(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Comment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function inboxes(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Inbox::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Message::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
app/Notifications/NewContactNotification.php
Normal file
37
app/Notifications/NewContactNotification.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class NewContactNotification extends Notification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $customerName,
|
||||||
|
private readonly string $customerEmail,
|
||||||
|
private readonly string $customerMessage
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via($notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail($notifiable): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('New Contact Submission: '.$this->customerName)
|
||||||
|
->greeting('Hello Admin,') // Or keep it empty if you prefer
|
||||||
|
->line('You have received a new message from your contact form:')
|
||||||
|
->line("**Name:** {$this->customerName}")
|
||||||
|
->line("**Email:** {$this->customerEmail}")
|
||||||
|
->line('**Message:**')
|
||||||
|
->line($this->customerMessage)
|
||||||
|
->action('Reply via Email', 'mailto:'.$this->customerEmail);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Notifications/NewDealNotification.php
Normal file
40
app/Notifications/NewDealNotification.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Models\Deal;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use NotificationChannels\WebPush\WebPushChannel;
|
||||||
|
use NotificationChannels\WebPush\WebPushMessage;
|
||||||
|
|
||||||
|
class NewDealNotification extends Notification
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
{
|
||||||
|
\Log::info('Building WebPush for user: '.$notifiable->id);
|
||||||
|
|
||||||
|
return (new WebPushMessage)
|
||||||
|
->title("New deal from {$this->deal->broker->name}")
|
||||||
|
->body('Check out this deal:'.Str::limit($this->deal->title, 30, '...'))
|
||||||
|
->action('View deal', route('explore', ['show' => $this->deal->id]));
|
||||||
|
}
|
||||||
|
}
|
||||||
43
app/Notifications/ReportRejectedNotificationToUser.php
Normal file
43
app/Notifications/ReportRejectedNotificationToUser.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class ReportRejectedNotificationToUser extends Notification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $dealTitle,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via($notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail($notifiable): MailMessage
|
||||||
|
{
|
||||||
|
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('Update on Your Recent Report: '.$this->dealTitle)
|
||||||
|
->greeting('Hello!')
|
||||||
|
->line('Thank you for helping us maintain the integrity of our marketplace.')
|
||||||
|
->line("We have completed our review of the deal you reported: **{$this->dealTitle}**.")
|
||||||
|
->line('Based on our moderation policy, we have rejected your report.')
|
||||||
|
->action('View Marketplace', route('explore'))
|
||||||
|
->line('Your feedback helps make our community a safer place for everyone.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray($notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'report_outcome' => $this->isContentRemoved ? 'violation_confirmed' : 'no_violation_found',
|
||||||
|
'deal_title' => $this->dealTitle,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/Notifications/ReportResolvedNotificationToBroker.php
Normal file
46
app/Notifications/ReportResolvedNotificationToBroker.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class ReportResolvedNotificationToBroker extends Notification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $dealTitle,
|
||||||
|
private readonly bool $isContentRemoved
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via($notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail($notifiable): MailMessage
|
||||||
|
{
|
||||||
|
$status = $this->isContentRemoved
|
||||||
|
? 'has been removed following a policy review.'
|
||||||
|
: 'has been reviewed and remains active.';
|
||||||
|
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('Update Regarding Your Reported Deal: '.$this->dealTitle)
|
||||||
|
->greeting('Hello!')
|
||||||
|
->line("We are writing to inform you that the report regarding your deal, **{$this->dealTitle}**, has been resolved.")
|
||||||
|
->line("Our moderation team has completed their review, and the content {$status}")
|
||||||
|
->action('View My Deals', route('broker.dashboard')) // Adjusted for your UMS/Project structure
|
||||||
|
->line('Thank you for being a part of our marketplace.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray($notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'deal_title' => $this->dealTitle,
|
||||||
|
'action_taken' => $this->isContentRemoved ? 'removed' : 'kept',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Notifications/ReportResolvedNotificationToUser.php
Normal file
47
app/Notifications/ReportResolvedNotificationToUser.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class ReportResolvedNotificationToUser extends Notification implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $dealTitle,
|
||||||
|
private readonly bool $isContentRemoved
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function via($notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail($notifiable): MailMessage
|
||||||
|
{
|
||||||
|
$outcome = $this->isContentRemoved
|
||||||
|
? 'has been removed following our investigation.'
|
||||||
|
: 'will remain active as it was found to be in compliance with our guidelines.';
|
||||||
|
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('Update on Your Recent Report: '.$this->dealTitle)
|
||||||
|
->greeting('Hello!')
|
||||||
|
->line('Thank you for helping us maintain the integrity of our marketplace.')
|
||||||
|
->line("We have completed our review of the deal you reported: **{$this->dealTitle}**.")
|
||||||
|
->line("Based on our moderation policy, the content {$outcome}")
|
||||||
|
->action('View Marketplace', route('explore'))
|
||||||
|
->line('Your feedback helps make our community a safer place for everyone.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray($notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'report_outcome' => $this->isContentRemoved ? 'violation_confirmed' : 'no_violation_found',
|
||||||
|
'deal_title' => $this->dealTitle,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/Queries/ExplorePageDealsQuery.php
Normal file
35
app/Queries/ExplorePageDealsQuery.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Queries;
|
||||||
|
|
||||||
|
use App\Models\Deal;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
final readonly class ExplorePageDealsQuery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return Builder<Deal>
|
||||||
|
*/
|
||||||
|
public function builder(): Builder
|
||||||
|
{
|
||||||
|
return Deal::query()
|
||||||
|
->select([
|
||||||
|
'id', 'title', 'description', 'image', 'active', 'slug', 'link',
|
||||||
|
'deal_category_id', 'user_id',
|
||||||
|
])
|
||||||
|
// Select additional details
|
||||||
|
->with([
|
||||||
|
'category:id,name',
|
||||||
|
'broker' => function ($query) {
|
||||||
|
$query->select('id', 'name', 'email', 'role_type', 'role_id')
|
||||||
|
->with('type');
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Check if the current user interacted with the deal
|
||||||
|
->tap(fn ($q) => (new Deal)->withCurrentUserInteractions($q))
|
||||||
|
->tap(fn ($q) => (new Deal)->withLikePerDeal($q))
|
||||||
|
->tap(fn ($q) => (new Deal)->withIsFollowedByCurrentUser($q))
|
||||||
|
->tap(fn ($q) => (new Deal)->withRedirectionPerDeal($q));
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/Queries/PageVisitStatsQuery.php
Normal file
22
app/Queries/PageVisitStatsQuery.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Queries;
|
||||||
|
|
||||||
|
use App\Enums\UserTypes;
|
||||||
|
use App\Models\PageVisit;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
final readonly class PageVisitStatsQuery
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return Builder<PageVisit>
|
||||||
|
*/
|
||||||
|
public function builder(UserTypes $userType, string $startDay, string $endDay): Builder
|
||||||
|
{
|
||||||
|
return PageVisit::query()
|
||||||
|
->selectRaw('count(distinct user_id) as user_count, created_at as date')
|
||||||
|
->where('user_type', $userType)
|
||||||
|
->whereBetween('created_at', [$startDay.' 00:00:00', $endDay.' 23:59:59'])
|
||||||
|
->groupBy('date');
|
||||||
|
}
|
||||||
|
}
|
||||||
29
app/Services/OTPService.php
Normal file
29
app/Services/OTPService.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class OTPService
|
||||||
|
{
|
||||||
|
public function generate(User $user, int $length = 6): string
|
||||||
|
{
|
||||||
|
$code = \Str::random($length);
|
||||||
|
Cache::put("otp_$user->id", $code, now()->addMinutes((int) config('auth.otp_lifespan', '10')));
|
||||||
|
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function verify(User $user, string $otp): bool
|
||||||
|
{
|
||||||
|
$code = Cache::get("otp_$user->id");
|
||||||
|
if ($code === $otp) {
|
||||||
|
Cache::forget("otp_$user->id");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
app/Services/ProfileInitialsService.php
Normal file
20
app/Services/ProfileInitialsService.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class ProfileInitialsService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the initials from a full name (e.g. John Doe, Alex Mark, jane clerk)
|
||||||
|
* to display on the profile page (e.g. JD, AM, JC).
|
||||||
|
*/
|
||||||
|
public function create(string $fullname)
|
||||||
|
{
|
||||||
|
return Str::of($fullname)
|
||||||
|
->explode(' ')
|
||||||
|
->map(fn ($word) => Str::substr(ucfirst($word), 0, 1))
|
||||||
|
->join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
24
app/Services/TwilioService.php
Normal file
24
app/Services/TwilioService.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Twilio\Exceptions\TwilioException;
|
||||||
|
use Twilio\Rest\Client;
|
||||||
|
|
||||||
|
class TwilioService
|
||||||
|
{
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->client = new Client(config('services.twilio.sid'), config('services.twilio.token'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws TwilioException
|
||||||
|
*/
|
||||||
|
public function sendSms(string $to, string $message): void
|
||||||
|
{
|
||||||
|
$this->client->messages->create($to, ['from' => config('services.twilio.from'), 'body' => $message]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,6 +8,7 @@
|
|||||||
->withRouting(
|
->withRouting(
|
||||||
web: __DIR__.'/../routes/web.php',
|
web: __DIR__.'/../routes/web.php',
|
||||||
commands: __DIR__.'/../routes/console.php',
|
commands: __DIR__.'/../routes/console.php',
|
||||||
|
channels: __DIR__.'/../routes/channels.php',
|
||||||
health: '/up',
|
health: '/up',
|
||||||
)
|
)
|
||||||
->withMiddleware(function (Middleware $middleware): void {
|
->withMiddleware(function (Middleware $middleware): void {
|
||||||
|
|||||||
2
bootstrap/cache/.gitignore
vendored
2
bootstrap/cache/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
105
bootstrap/cache/packages.php
vendored
Executable file
105
bootstrap/cache/packages.php
vendored
Executable file
@ -0,0 +1,105 @@
|
|||||||
|
<?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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'laradumps/laradumps' =>
|
||||||
|
array (
|
||||||
|
'providers' =>
|
||||||
|
array (
|
||||||
|
0 => 'LaraDumps\\LaraDumps\\LaraDumpsServiceProvider',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'laravel-notification-channels/webpush' =>
|
||||||
|
array (
|
||||||
|
'providers' =>
|
||||||
|
array (
|
||||||
|
0 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'laravel/pail' =>
|
||||||
|
array (
|
||||||
|
'providers' =>
|
||||||
|
array (
|
||||||
|
0 => 'Laravel\\Pail\\PailServiceProvider',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'laravel/reverb' =>
|
||||||
|
array (
|
||||||
|
'providers' =>
|
||||||
|
array (
|
||||||
|
0 => 'Laravel\\Reverb\\ApplicationManagerServiceProvider',
|
||||||
|
1 => 'Laravel\\Reverb\\ReverbServiceProvider',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'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',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
289
bootstrap/cache/services.php
vendored
Executable file
289
bootstrap/cache/services.php
vendored
Executable file
@ -0,0 +1,289 @@
|
|||||||
|
<?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 => 'LaraDumps\\LaraDumps\\LaraDumpsServiceProvider',
|
||||||
|
27 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
|
||||||
|
28 => 'Laravel\\Pail\\PailServiceProvider',
|
||||||
|
29 => 'Laravel\\Reverb\\ApplicationManagerServiceProvider',
|
||||||
|
30 => 'Laravel\\Reverb\\ReverbServiceProvider',
|
||||||
|
31 => 'Laravel\\Sail\\SailServiceProvider',
|
||||||
|
32 => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||||
|
33 => 'Carbon\\Laravel\\ServiceProvider',
|
||||||
|
34 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||||
|
35 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||||
|
36 => 'Opcodes\\LogViewer\\LogViewerServiceProvider',
|
||||||
|
37 => 'Pest\\Laravel\\PestServiceProvider',
|
||||||
|
38 => '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 => 'LaraDumps\\LaraDumps\\LaraDumpsServiceProvider',
|
||||||
|
13 => 'NotificationChannels\\WebPush\\WebPushServiceProvider',
|
||||||
|
14 => 'Laravel\\Pail\\PailServiceProvider',
|
||||||
|
15 => 'Laravel\\Reverb\\ReverbServiceProvider',
|
||||||
|
16 => 'Carbon\\Laravel\\ServiceProvider',
|
||||||
|
17 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||||
|
18 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||||
|
19 => 'Opcodes\\LogViewer\\LogViewerServiceProvider',
|
||||||
|
20 => 'Pest\\Laravel\\PestServiceProvider',
|
||||||
|
21 => '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\\Reverb\\ApplicationManager' => 'Laravel\\Reverb\\ApplicationManagerServiceProvider',
|
||||||
|
'Laravel\\Reverb\\Contracts\\ApplicationProvider' => 'Laravel\\Reverb\\ApplicationManagerServiceProvider',
|
||||||
|
'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\\Reverb\\ApplicationManagerServiceProvider' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'Laravel\\Sail\\SailServiceProvider' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'Laravel\\Tinker\\TinkerServiceProvider' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
@ -11,10 +11,14 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"blade-ui-kit/blade-heroicons": "^2.6",
|
"blade-ui-kit/blade-heroicons": "^2.6",
|
||||||
|
"laravel-notification-channels/webpush": "^10.4",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1"
|
"laravel/reverb": "^1.0",
|
||||||
|
"laravel/tinker": "^2.10.1",
|
||||||
|
"twilio/sdk": "^8.10"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"barryvdh/laravel-ide-helper": "^3.6",
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
"laradumps/laradumps": "^5.0",
|
"laradumps/laradumps": "^5.0",
|
||||||
"laravel/pail": "^1.2.2",
|
"laravel/pail": "^1.2.2",
|
||||||
@ -22,6 +26,7 @@
|
|||||||
"laravel/sail": "^1.41",
|
"laravel/sail": "^1.41",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
|
"opcodesio/log-viewer": "^3.21",
|
||||||
"pestphp/pest": "^4.3",
|
"pestphp/pest": "^4.3",
|
||||||
"pestphp/pest-plugin-laravel": "^4.0"
|
"pestphp/pest-plugin-laravel": "^4.0"
|
||||||
},
|
},
|
||||||
|
|||||||
2487
composer.lock
generated
2487
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -111,5 +111,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
|
||||||
|
'otp_lifespan' => env('OTP_LIFESPAN', 10),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
82
config/broadcasting.php
Normal file
82
config/broadcasting.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Broadcaster
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default broadcaster that will be used by the
|
||||||
|
| framework when an event needs to be broadcast. You may set this to
|
||||||
|
| any of the connections defined in the "connections" array below.
|
||||||
|
|
|
||||||
|
| Supported: "reverb", "pusher", "ably", "redis", "log", "null"
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('BROADCAST_CONNECTION', 'null'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Broadcast Connections
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define all of the broadcast connections that will be used
|
||||||
|
| to broadcast events to other systems or over WebSockets. Samples of
|
||||||
|
| each available type of connection are provided inside this array.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'connections' => [
|
||||||
|
|
||||||
|
'reverb' => [
|
||||||
|
'driver' => 'reverb',
|
||||||
|
'key' => env('REVERB_APP_KEY'),
|
||||||
|
'secret' => env('REVERB_APP_SECRET'),
|
||||||
|
'app_id' => env('REVERB_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'host' => env('REVERB_HOST'),
|
||||||
|
'port' => env('REVERB_PORT', 443),
|
||||||
|
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||||
|
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'client_options' => [
|
||||||
|
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'pusher' => [
|
||||||
|
'driver' => 'pusher',
|
||||||
|
'key' => env('PUSHER_APP_KEY'),
|
||||||
|
'secret' => env('PUSHER_APP_SECRET'),
|
||||||
|
'app_id' => env('PUSHER_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'cluster' => env('PUSHER_APP_CLUSTER'),
|
||||||
|
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
|
||||||
|
'port' => env('PUSHER_PORT', 443),
|
||||||
|
'scheme' => env('PUSHER_SCHEME', 'https'),
|
||||||
|
'encrypted' => true,
|
||||||
|
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'client_options' => [
|
||||||
|
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'ably' => [
|
||||||
|
'driver' => 'ably',
|
||||||
|
'key' => env('ABLY_KEY'),
|
||||||
|
],
|
||||||
|
|
||||||
|
'log' => [
|
||||||
|
'driver' => 'log',
|
||||||
|
],
|
||||||
|
|
||||||
|
'null' => [
|
||||||
|
'driver' => 'null',
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
95
config/reverb.php
Normal file
95
config/reverb.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default Reverb Server
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This option controls the default server used by Reverb to handle
|
||||||
|
| incoming messages as well as broadcasting message to all your
|
||||||
|
| connected clients. At this time only "reverb" is supported.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'default' => env('REVERB_SERVER', 'reverb'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reverb Servers
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define details for each of the supported Reverb servers.
|
||||||
|
| Each server has its own configuration options that are defined in
|
||||||
|
| the array below. You should ensure all the options are present.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'servers' => [
|
||||||
|
|
||||||
|
'reverb' => [
|
||||||
|
'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
|
||||||
|
'port' => env('REVERB_SERVER_PORT', 8080),
|
||||||
|
'path' => env('REVERB_SERVER_PATH', ''),
|
||||||
|
'hostname' => env('REVERB_HOST'),
|
||||||
|
'options' => [
|
||||||
|
'tls' => [],
|
||||||
|
],
|
||||||
|
'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
|
||||||
|
'scaling' => [
|
||||||
|
'enabled' => env('REVERB_SCALING_ENABLED', false),
|
||||||
|
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
|
||||||
|
'server' => [
|
||||||
|
'url' => env('REDIS_URL'),
|
||||||
|
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||||
|
'port' => env('REDIS_PORT', '6379'),
|
||||||
|
'username' => env('REDIS_USERNAME'),
|
||||||
|
'password' => env('REDIS_PASSWORD'),
|
||||||
|
'database' => env('REDIS_DB', '0'),
|
||||||
|
'timeout' => env('REDIS_TIMEOUT', 60),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
|
||||||
|
'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15),
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Reverb Applications
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you may define how Reverb applications are managed. If you choose
|
||||||
|
| to use the "config" provider, you may define an array of apps which
|
||||||
|
| your server will support, including their connection credentials.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'apps' => [
|
||||||
|
|
||||||
|
'provider' => 'config',
|
||||||
|
|
||||||
|
'apps' => [
|
||||||
|
[
|
||||||
|
'key' => env('REVERB_APP_KEY'),
|
||||||
|
'secret' => env('REVERB_APP_SECRET'),
|
||||||
|
'app_id' => env('REVERB_APP_ID'),
|
||||||
|
'options' => [
|
||||||
|
'host' => env('REVERB_HOST'),
|
||||||
|
'port' => env('REVERB_PORT', 443),
|
||||||
|
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||||
|
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
|
||||||
|
],
|
||||||
|
'allowed_origins' => ['*'],
|
||||||
|
'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
|
||||||
|
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
|
||||||
|
'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'),
|
||||||
|
'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user