diff --git a/app/Http/Controllers/NotificationProbeController.php b/app/Http/Controllers/NotificationProbeController.php new file mode 100644 index 0000000..841f609 --- /dev/null +++ b/app/Http/Controllers/NotificationProbeController.php @@ -0,0 +1,18 @@ +user()->notify(new UniqueContact(Deal::find(4))); + echo auth()->user()->unreadNotifications->count() . '
'; + die(auth()->user()->notifications); + } +} diff --git a/app/Modules/Admin/Views/complexes/edit.blade.php b/app/Modules/Admin/Views/complexes/edit.blade.php index 5eefab7..853c256 100644 --- a/app/Modules/Admin/Views/complexes/edit.blade.php +++ b/app/Modules/Admin/Views/complexes/edit.blade.php @@ -1,8 +1,11 @@ -@php($title = 'Жилые комплексы') +@php($title = 'Жилой комплекс «' . $complex->name . '»') +@php($backUrl = route('admin.complexes')) @extends('layouts.admin') @section('content')
-

{{ $complex->name }}

+

+ Редактирование +

@csrf @@ -28,6 +31,8 @@
+ Отмена + @endsection diff --git a/app/Modules/Bot/Config/config.php b/app/Modules/Bot/Config/config.php new file mode 100644 index 0000000..192ee2c --- /dev/null +++ b/app/Modules/Bot/Config/config.php @@ -0,0 +1,14 @@ + '8108664710:AAG4sNtP02P0a6ElSHpCypsYV77EZCADwSo', + 'telegram_bot_identifier' => 'testnaviomiro38_bot', + 'telegram_bot_id' => 1, + 'telegram_type' => 'Telegram', + + 'vk_bot_id' => 2, + 'vk_type' => 'Vk', + 'vk_group_token' => 'vk1.a.s1l2J2Nri9FukE8m-eIAjq1fgBMXO_O5WR6T-cCoDjetrY9XrVggY2JyhGDv5pcTeJQuX16OxUrcCaTLagjIyp1w1A6CXKstsCPcnIxDm0GATK15360lhQ5SFjT0ciYh809wf22cQbFssHkkQj6shGzhVvJC7rdpjXNmdMyPs2geiaxktWjEVM7-2oIUrcOrs-zeGxKeucJ7_ofstL4g0A', + 'vk_group_id' => 227931308, + 'vk_group_confirmation' => '1bb6fd31' +]; diff --git a/app/Modules/Bot/Database/Migrations/2024_10_23_142253_create_messenger_bots_table.php b/app/Modules/Bot/Database/Migrations/2024_10_23_142253_create_messenger_bots_table.php new file mode 100644 index 0000000..b124d35 --- /dev/null +++ b/app/Modules/Bot/Database/Migrations/2024_10_23_142253_create_messenger_bots_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('type'); + $table->timestamps(); + }); + + // Добавляем запись с type "Telegram" + DB::table('messenger_bots')->insertOrIgnore([ + 'type' => 'Telegram', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + + + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('messenger_bots'); + } +}; diff --git a/app/Modules/Bot/Database/Migrations/2024_10_23_142254_add_vk_in_messenger_bots_table.php b/app/Modules/Bot/Database/Migrations/2024_10_23_142254_add_vk_in_messenger_bots_table.php new file mode 100644 index 0000000..94e232b --- /dev/null +++ b/app/Modules/Bot/Database/Migrations/2024_10_23_142254_add_vk_in_messenger_bots_table.php @@ -0,0 +1,29 @@ +insertOrIgnore([ + 'type' => 'Vk', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('messenger_bots')->where('type', 'Vk')->delete(); + } +}; diff --git a/app/Modules/Bot/Database/Migrations/2024_10_23_142323_create_user_bots_table.php b/app/Modules/Bot/Database/Migrations/2024_10_23_142323_create_user_bots_table.php new file mode 100644 index 0000000..6997f9c --- /dev/null +++ b/app/Modules/Bot/Database/Migrations/2024_10_23_142323_create_user_bots_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('bot_id')->constrained( + table: 'messenger_bots', + indexName: 'messenger_bots_bot_id_foreign_key' + )->onDelete('cascade'); + $table->foreignId('user_id')->constrained( + table: 'users', + indexName: 'users_user_id_foreign_key' + )->onDelete('cascade'); + $table->string('user_bot_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_bots'); + } +}; diff --git a/app/Modules/Bot/Http/Controllers/BotNotificationSender.php b/app/Modules/Bot/Http/Controllers/BotNotificationSender.php new file mode 100644 index 0000000..490c3ec --- /dev/null +++ b/app/Modules/Bot/Http/Controllers/BotNotificationSender.php @@ -0,0 +1,28 @@ +with('bot')->get(); + + foreach ($userBots as $userBot) { + switch ($userBot->bot->type) { + case config('bot.telegram_type'): + TelegramBot::sendMessage($userBot->user_bot_id, $message); + break; + case config('bot.vk_type'): + VkBot::sendMessage($userBot->user_bot_id, $message); + break; + default: + Log::warning("Unknown bot type: {$userBot->bot->type}"); + break; + } + } + } +} diff --git a/app/Modules/Bot/Http/Controllers/TelegramBot.php b/app/Modules/Bot/Http/Controllers/TelegramBot.php new file mode 100644 index 0000000..21e0298 --- /dev/null +++ b/app/Modules/Bot/Http/Controllers/TelegramBot.php @@ -0,0 +1,161 @@ +botIdentifier = config('bot.telegram_bot_identifier'); + $this->botToken = config('bot.telegram_bot_token'); + } + + public function handleWebhook(Request $request) + { + $update = $request->all(); + + if (isset($update['message'])) { + $this->handleMessage($update['message']); + } elseif (isset($update['my_chat_member'])) { + $this->handleChatMemberUpdate($update['my_chat_member']); + } + } + + private function handleMessage($message) + { + if (isset($message['text'])) { + $text = $message['text']; + $chatId = $message['chat']['id']; + $command = strtolower(explode(' ', $text)[0]); + + switch ($command) { + case '/start': + $this->handleStart($text, $chatId); + break; + case '/stop': + $this->handleUnsubscribe($chatId); + break; + } + } + } + + private function handleChatMemberUpdate($chatMember) + { + $chatId = $chatMember['chat']['id']; + $newStatus = $chatMember['new_chat_member']['status']; + + if ($newStatus === 'kicked' || $newStatus === 'left') { + $this->stopBot($chatId); + } + } + + private function handleStart($text, $chatId) + { + $parts = explode(' ', $text); + if (count($parts) > 1) { + $encryptedUserId = $parts[1]; + try { + $userId = base64_decode($encryptedUserId); + + $user = User::find($userId); + if ($user) { + UserBot::updateOrCreate( + ['user_id' => $userId, 'bot_id' => config('bot.telegram_bot_id')], + ['user_bot_id' => $chatId] + ); + + $this->sendMessage($chatId, "Здравствуйте, {$user->name}! Вы успешно подписались на уведомления."); + } else { + $this->sendMessage($chatId, "Извините, не удалось найти вашу учетную запись."); + } + } catch (\Exception $e) { + Log::error('Error decrypting user ID', ['error' => $e->getMessage()]); + $this->sendMessage($chatId, "Произошла ошибка при обработке вашего запроса."); + } + } else { + $this->sendMessage($chatId, "Добро пожаловать! Для подписки на уведомления, пожалуйста, используйте ссылку с нашего сайта."); + } + } + + private function stopBot($chatId) + { + try { + $userBot = UserBot::where('user_bot_id', $chatId) + ->where('bot_id', config('bot.telegram_bot_id')) + ->first(); + + if ($userBot) { + $userBot->delete(); + Log::info('User unsubscribed', ['chatId' => $chatId]); + } else { + Log::info('Unsubscribe attempt for non-subscribed user', ['chatId' => $chatId]); + } + } catch (\Exception $e) { + Log::error('Error during unsubscribe process', ['error' => $e->getMessage(), 'chatId' => $chatId]); + } + } + + private function handleUnsubscribe($chatId) + { + try { + $userBot = UserBot::where('user_bot_id', $chatId) + ->where('bot_id', config('bot.telegram_bot_id')) + ->first(); + + if ($userBot) { + $userBot->delete(); + $this->sendMessage($chatId, "Вы успешно отписались от уведомлений. Если захотите подписаться снова, воспользуйтесь ссылкой на нашем сайте."); + Log::info('User unsubscribed', ['chatId' => $chatId]); + } else { + $this->sendMessage($chatId, "Вы не были подписаны на уведомления."); + Log::info('Unsubscribe attempt for non-subscribed user', ['chatId' => $chatId]); + } + } catch (\Exception $e) { + Log::error('Error during unsubscribe process', ['error' => $e->getMessage(), 'chatId' => $chatId]); + $this->sendMessage($chatId, "Произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже."); + } + } + + public function subscribe() :mixed + { + $userId = Auth::id(); + $encryptedUserId = base64_encode($userId); + + $telegramUrl = "https://t.me/$this->botIdentifier?start=$encryptedUserId"; + + return redirect($telegramUrl); + } + + + public static function sendMessage(string $userBotId, string $message): void + { + $botToken = config('bot.telegram_bot_token'); + Http::withoutVerifying() + ->post("https://api.telegram.org/bot{$botToken}/sendMessage", [ + 'chat_id' => $userBotId, + 'text' => $message, + 'parse_mode' => 'HTML' + ]); + } + + public function setHook() + { + $response = Http::withoutVerifying() + ->post("https://api.telegram.org/bot{$this->botToken}/setWebhook", [ + 'url' => 'https://naviom.iro38.ru/api/telegramWebhook', + 'allowed_updates' => ['message', 'my_chat_member'] + ])->json(); + dd($response); + } +} \ No newline at end of file diff --git a/app/Modules/Bot/Http/Controllers/VkBot.php b/app/Modules/Bot/Http/Controllers/VkBot.php new file mode 100644 index 0000000..c1276de --- /dev/null +++ b/app/Modules/Bot/Http/Controllers/VkBot.php @@ -0,0 +1,75 @@ +botId = config('bot.vk_bot_id'); + $this->groupId = config('bot.vk_group_id'); + $this->groupConfirmation = config('bot.vk_group_confirmation'); + } + + public static function sendMessage(string $userBotId, string $message): void + { + Log::info('Попытка отправить сообщение'); + $params['access_token'] = config('bot.vk_group_token'); + $params['v'] = '5.199'; + $params['random_id'] = random_int(1, PHP_INT_MAX); + $params['user_id'] = $userBotId; + $params['message'] = $message; + + $response = Http::withoutVerifying()->withQueryParameters($params)->post("https://api.vk.com/method/messages.send"); + } + + public function allowMessages(Request $request) + { + if ($request->vkUserId and $request->naviomUserId and User::find($request->naviomUserId)) { + UserBot::updateOrCreate( + ['user_id' => $request->naviomUserId, 'bot_id' => $this->botId], + ['user_bot_id' => $request->vkUserId] + ); + $user = User::find($request->naviomUserId); + $this->sendMessage($request->vkUserId, "Здравствуйте, {$user->name}! Вы успешно подписались на уведомления."); + } + return response()->json(['success' => true]); + } + + public function handleVkCallback(Request $request) + { + switch ($request->type) { + case 'confirmation': + return $this->handleConfirmation($request->group_id); + case 'message_deny': + return $this->handleDenyMessage($request->object['user_id']); + } + } + + private function handleConfirmation(int $groupId): string + { + if ($groupId == $this->groupId) { + return $this->groupConfirmation; + } + } + + private function handleDenyMessage(int $vkUserId): string + { + $subscription = UserBot::where('user_bot_id', $vkUserId)->where('bot_id', $this->botId)->first(); + if ($subscription) { + $subscription->delete(); + } + return 'ok'; + } +} diff --git a/app/Modules/Bot/Http/Interfaces/MessengerBotInterface.php b/app/Modules/Bot/Http/Interfaces/MessengerBotInterface.php new file mode 100644 index 0000000..496c67a --- /dev/null +++ b/app/Modules/Bot/Http/Interfaces/MessengerBotInterface.php @@ -0,0 +1,9 @@ +belongsTo(MessengerBot::class); + } +} diff --git a/app/Modules/Bot/Providers/ModuleServiceProvider.php b/app/Modules/Bot/Providers/ModuleServiceProvider.php new file mode 100644 index 0000000..24010d5 --- /dev/null +++ b/app/Modules/Bot/Providers/ModuleServiceProvider.php @@ -0,0 +1,68 @@ +app->register(RouteServiceProvider::class); + } + + public function boot() + { + $this->registerViews(); + $this->registerLivewireViews(); + $this->registerMigrations(); + $this->registerConfig(); + $this->registerComponent(); + $this->registerLivewire(); + } + + protected function registerViews() + { + $moduleViewsPath = __DIR__.'/../Views'; + $this->loadViewsFrom( + $moduleViewsPath, strtolower($this->moduleName) + ); + } + + protected function registerLivewireViews() + { + $moduleViewsPath = __DIR__.'/../Views/livewire'; + $this->loadViewsFrom( + $moduleViewsPath, strtolower($this->moduleName) + ); + } + + protected function registerMigrations() + { + $this->loadMigrationsFrom( + app_path('Modules/'.$this->moduleName.'/Database/Migrations') + ); + } + + protected function registerConfig() + { + $path = app_path('Modules/'.$this->moduleName.'/Config/config.php'); + $this->mergeConfigFrom( + $path, strtolower($this->moduleName) + ); + } + + protected function registerLivewire() + { + //Livewire::component('', \Modules\\Http\Livewire\::class); + } + + protected function registerComponent() + { + //Blade::component('', \Modules\\Http\Components\::class); + } +} \ No newline at end of file diff --git a/app/Modules/Bot/Providers/RouteServiceProvider.php b/app/Modules/Bot/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..9568983 --- /dev/null +++ b/app/Modules/Bot/Providers/RouteServiceProvider.php @@ -0,0 +1,24 @@ +registerWebRoutes(); + } + + protected function registerWebRoutes() + { + //Add Web Routes with web Guard + Route::middleware('web') + //Set Default Controllers Namespace + ->namespace('Modules\\Bot\\Http\\Controllers') + ->group(app_path('Modules/Bot/Routes/web.php')); + } +} \ No newline at end of file diff --git a/app/Modules/Bot/Routes/web.php b/app/Modules/Bot/Routes/web.php new file mode 100644 index 0000000..79d0cfd --- /dev/null +++ b/app/Modules/Bot/Routes/web.php @@ -0,0 +1,20 @@ +group(function() { + + Route::get('/telegram/subscribe',[TelegramBot::class, 'subscribe'])->name('telegram.subscribe'); + Route::get('/telegramHook', [TelegramBot::class, 'setHook'])->name('setHook'); + + Route::post('/vk/allowMessages',[VkBot::class, 'allowMessages'])->name('vk.allowMessages'); + + + Route::middleware(['hasAccess'])->group(function() { + /** Routes that need to be protected - Маршруты которые нужно защитить */ + }); +}); + diff --git a/app/Modules/Bot/Views/index.blade.php b/app/Modules/Bot/Views/index.blade.php new file mode 100644 index 0000000..f8198cf --- /dev/null +++ b/app/Modules/Bot/Views/index.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.app') + @section('content') +

Example views

+ @endsection \ No newline at end of file diff --git a/app/Modules/Notice/Config/config.php b/app/Modules/Notice/Config/config.php new file mode 100644 index 0000000..ce09543 --- /dev/null +++ b/app/Modules/Notice/Config/config.php @@ -0,0 +1,5 @@ + 'NewRecomendationNotice', +]; \ No newline at end of file diff --git a/app/Modules/Notice/Database/Migrations/2025_05_08_160549_create_notifications_table.php b/app/Modules/Notice/Database/Migrations/2025_05_08_160549_create_notifications_table.php new file mode 100644 index 0000000..7eba854 --- /dev/null +++ b/app/Modules/Notice/Database/Migrations/2025_05_08_160549_create_notifications_table.php @@ -0,0 +1,40 @@ +id(); + $table->foreignId('user_id')->constrained( + table: 'users', indexName: 'notifications_user_id_foreign_key' + )->onDelete('cascade'); + $table->string('action'); + $table->string('object_name'); + $table->unsignedInteger('object_id'); + $table->date('read_at')->nullable(); + $table->date('sended_to_email_at')->nullable(); + $table->date('disabled_at')->nullable(); + $table->timestamps(); + }); + + Schema::table('notifications', function (Blueprint $table) { + $table->unique(['user_id', 'action', 'object_name', 'object_id']); + });*/ + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + //Schema::dropIfExists('notifications'); + } +}; diff --git a/app/Modules/Notice/Http/Controllers/NoticeController.php b/app/Modules/Notice/Http/Controllers/NoticeController.php new file mode 100644 index 0000000..a6a2c43 --- /dev/null +++ b/app/Modules/Notice/Http/Controllers/NoticeController.php @@ -0,0 +1,25 @@ +run(); + } +} \ No newline at end of file diff --git a/app/Modules/Notice/Http/Livewire/UserNotices.php b/app/Modules/Notice/Http/Livewire/UserNotices.php new file mode 100644 index 0000000..0f3fdc4 --- /dev/null +++ b/app/Modules/Notice/Http/Livewire/UserNotices.php @@ -0,0 +1,23 @@ +markAsRead(); + } + + public function render() + { + $currentDate = date("Y-m-d"); + $startDate = date("Y-m-d", strtotime($currentDate . " -2 months")); + return view('notice::user-notifications', [ + 'notifications' => auth()->user()->notifications + ]); + } +} diff --git a/app/Modules/Notice/Http/Livewire/UserNoticesButton.php b/app/Modules/Notice/Http/Livewire/UserNoticesButton.php new file mode 100644 index 0000000..3a58a27 --- /dev/null +++ b/app/Modules/Notice/Http/Livewire/UserNoticesButton.php @@ -0,0 +1,23 @@ + + auth()->user()->unreadNotifications + ->count() + ]); + } +} diff --git a/app/Modules/Notice/Models/NewWayMeetingNotice.php b/app/Modules/Notice/Models/NewWayMeetingNotice.php new file mode 100644 index 0000000..41437b7 --- /dev/null +++ b/app/Modules/Notice/Models/NewWayMeetingNotice.php @@ -0,0 +1,22 @@ +object_id); + if (!$wayMeeting) + return false; + return [ + 'id' => $wayMeeting->id, + 'name' => $wayMeeting->meeting->name, + 'action' => route('way.index', ['way' => $wayMeeting->way->id]), + ]; + } +} \ No newline at end of file diff --git a/app/Modules/Notice/Models/Notice.php b/app/Modules/Notice/Models/Notice.php new file mode 100644 index 0000000..94b5e18 --- /dev/null +++ b/app/Modules/Notice/Models/Notice.php @@ -0,0 +1,39 @@ +action $this->object_name"; + $noticeClass = __DIR__ . '/' . config('notice.types.' . $noticeName); + if (file_exists($noticeClass . '.php')) { + $noticeClass = 'Modules\\Notice\\Models\\' . config('notice.types.' . $noticeName); + if ($parameters = $noticeClass::getParameters($this)) { + return __("notification.$this->action $this->object_name", $parameters); + }; + }; + return FALSE; + // + } + + public function created_at() + { + return date_format(date_create($this->created_at), env('DATETIME_FORMAT')); + } + +} diff --git a/app/Modules/Notice/Models/NoticesMailer.php b/app/Modules/Notice/Models/NoticesMailer.php new file mode 100644 index 0000000..0a86e7e --- /dev/null +++ b/app/Modules/Notice/Models/NoticesMailer.php @@ -0,0 +1,72 @@ +subDays(1); + $date = $date->toDateTimeString(); + $latest_notice = Notice::where('created_at', '<=', $date) + ->whereNull('sended_to_email_at') + ->whereNull('read_at') + ->orderBy('created_at', 'desc'); + if ($latest_notice = $latest_notice->first()) { + $this->user = User::findOrFail($latest_notice->user_id); + $this->notices = Notice::where('user_id', $this->user->id) + ->whereNull('sended_to_email_at') + ->whereNull('read_at') + ->orderBy('object_name') + ->orderBy('created_at')->get(); + }; + return; + } + + public function run() + { + if (!$this->user) { + Log::error('Has no notices for mailing'); + return; + }; + + if ($this->notices->count() == 0) { + return false; + }; + + + $notification = new NoticesMailerNotification($this->notices); + + Notification::route('mail', $this->user->email) + ->notify($notification); + + $botMessage = $notification->buildBotMessage(); + BotNotificationSender::routeMessage($this->user->id, $botMessage); + + foreach ($this->notices as $notice) { + $notice->sended_to_email_at = now(); + $notice->save(); + }; + } +} diff --git a/app/Modules/Notice/Models/UnreadMessagesMailer.php b/app/Modules/Notice/Models/UnreadMessagesMailer.php new file mode 100644 index 0000000..0482ac8 --- /dev/null +++ b/app/Modules/Notice/Models/UnreadMessagesMailer.php @@ -0,0 +1,69 @@ +whereNull('read_at') + ->where('object_name', 'Chat') + ->orderBy('created_at'); + if ($newChatNotice = $newChatNotice->first()) { + $this->user = User::findOrFail($newChatNotice->user_id); + $this->notices = Notice::where('user_id', $this->user->id) + ->whereNull('sended_to_email_at') + ->whereNull('read_at') + ->where('object_name', 'Chat') + ->orderBy('created_at')->get(); + //$this->senders = ChatMessageRead::where('receiver_id', $this->user->id)->where('message.sender_id'); + }; + return; + } + + + public function run() + { + if (!$this->user) { + //Log::error('Has no notices for mailing'); + return; + }; + + if ($this->notices->count() == 0) { + return false; + }; + + $notification = new NewChatMessageNoticesMailerNotification(); + + Notification::route('mail', $this->user->email) + ->notify($notification); + + $botMessage = $notification->buildBotMessage(); + BotNotificationSender::routeMessage($this->user->id, $botMessage); + + foreach ($this->notices as $notice) { + $notice->sended_to_email_at = now(); + $notice->save(); + }; + } +} diff --git a/app/Modules/Notice/Providers/ModuleServiceProvider.php b/app/Modules/Notice/Providers/ModuleServiceProvider.php new file mode 100644 index 0000000..49f42fa --- /dev/null +++ b/app/Modules/Notice/Providers/ModuleServiceProvider.php @@ -0,0 +1,78 @@ +app->register(RouteServiceProvider::class); + } + + public function boot() + { + $this->registerViews(); + $this->registerLivewireViews(); + $this->registerMigrations(); + $this->registerConfig(); + $this->registerComponent(); + $this->registerLivewire(); + } + + protected function registerViews() + { + $moduleViewsPath = __DIR__ . '/../Views'; + $this->loadViewsFrom( + $moduleViewsPath, + strtolower($this->moduleName) + ); + } + + protected function registerLivewireViews() + { + $moduleViewsPath = __DIR__ . '/../Views/livewire'; + $this->loadViewsFrom( + $moduleViewsPath, + strtolower($this->moduleName) + ); + } + + protected function registerMigrations() + { + $this->loadMigrationsFrom( + app_path('Modules/' . $this->moduleName . '/Database/Migrations') + ); + } + + protected function registerConfig() + { + $path = app_path('Modules/' . $this->moduleName . '/Config/config.php'); + $this->mergeConfigFrom( + $path, + strtolower($this->moduleName) + ); + + $path = app_path('Modules/' . $this->moduleName . '/Config/notices.php'); + $this->mergeConfigFrom( + $path, + 'notice.types' + ); + } + + protected function registerLivewire() + { + Livewire::component('notices.user-notices', \Modules\Notice\Http\Livewire\UserNotices::class); + Livewire::component('notices.user-notices-button', \Modules\Notice\Http\Livewire\UserNoticesButton::class); + } + + protected function registerComponent() + { + //Blade::component('', \Modules\\Http\Components\::class); + } +} \ No newline at end of file diff --git a/app/Modules/Notice/Providers/RouteServiceProvider.php b/app/Modules/Notice/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..200ff8f --- /dev/null +++ b/app/Modules/Notice/Providers/RouteServiceProvider.php @@ -0,0 +1,24 @@ +registerWebRoutes(); + } + + protected function registerWebRoutes() + { + //Add Web Routes with web Guard + Route::middleware('web') + //Set Default Controllers Namespace + ->namespace('Modules\\Notice\\Http\\Controllers') + ->group(app_path('Modules/Notice/Routes/web.php')); + } +} \ No newline at end of file diff --git a/app/Modules/Notice/Routes/web.php b/app/Modules/Notice/Routes/web.php new file mode 100644 index 0000000..fdd07b7 --- /dev/null +++ b/app/Modules/Notice/Routes/web.php @@ -0,0 +1,14 @@ +group(function() { + + Route::get('/notice', [NoticeController::class, 'index']); + Route::get('/notice/run', [NoticeController::class, 'run']); + + Route::middleware(['hasAccess'])->group(function() { + /** Routes that need to be protected - Маршруты которые нужно защитить */ + }); +}); \ No newline at end of file diff --git a/app/Modules/Notice/Views/index.blade.php b/app/Modules/Notice/Views/index.blade.php new file mode 100644 index 0000000..bb7072c --- /dev/null +++ b/app/Modules/Notice/Views/index.blade.php @@ -0,0 +1,16 @@ +
+
+
+
Уведомления
+ +
+
+
+ @livewireStyles + + @livewireScripts +
+
+
+
diff --git a/app/Modules/Notice/Views/user-notices-button.blade.php b/app/Modules/Notice/Views/user-notices-button.blade.php new file mode 100644 index 0000000..066c6c1 --- /dev/null +++ b/app/Modules/Notice/Views/user-notices-button.blade.php @@ -0,0 +1,29 @@ + diff --git a/app/Modules/Notice/Views/user-notifications.blade.php b/app/Modules/Notice/Views/user-notifications.blade.php new file mode 100644 index 0000000..6213474 --- /dev/null +++ b/app/Modules/Notice/Views/user-notifications.blade.php @@ -0,0 +1,38 @@ +
+ @if ($notifications->count() == 0) +
+
+ + {{ __('notification.has no notifications') }} +
+
+ @else +
    + @foreach ($notifications as $notification) + @if (array_key_exists('text', $notification->data)) +
  • +
    +

    + {{ $notification->data['text'] }} +

    +

    + {{ $notification->created_at }} + @if ($notification->read_at) + + + + @else + + + + @endif +

    +
    +
  • + @endif + @endforeach +
+ @endif +
diff --git a/app/Notifications/UniqueContact.php b/app/Notifications/UniqueContact.php new file mode 100644 index 0000000..ddcabe1 --- /dev/null +++ b/app/Notifications/UniqueContact.php @@ -0,0 +1,62 @@ +status != DealStatus::UNIQUE) + { + //throw new \Exception('Notification sending: deal is not unique'); + } + $this->deal = $deal; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via(object $notifiable): array + { + return ['database']; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail(object $notifiable): MailMessage + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @return array + */ + public function toArray(object $notifiable): array + { + return [ + 'deal' => $this->deal->id, + 'text' => __('notifications.' . get_class($this), ['contact' => $this->deal->user->name]) + ]; + } +} diff --git a/database/migrations/2025_05_13_075115_create_notifications_table.php b/database/migrations/2025_05_13_075115_create_notifications_table.php new file mode 100644 index 0000000..d738032 --- /dev/null +++ b/database/migrations/2025_05_13_075115_create_notifications_table.php @@ -0,0 +1,31 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/lang/ru/notifications.php b/lang/ru/notifications.php new file mode 100644 index 0000000..7646881 --- /dev/null +++ b/lang/ru/notifications.php @@ -0,0 +1,18 @@ + 'Контакт :contact был проверен, уникальность подтверждена', + +]; diff --git a/resources/css/app.css b/resources/css/app.css index e488499..71af3d4 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -66,4 +66,45 @@ .active>.page-link { .page-item:not(.active)>.page-link:hover { color: #e6662a !important; border-color: #ccc +} + + +/*NOTOFICATIONS*/ +.notices-badge { + position: absolute; + top: 0; + right: 0; + left: 15px; + transform: translate(50%, -50%); + background-color: #d63939 !important +} + +.notices-badge:empty { + display: inline-block; + width: .5rem; + height: .5rem; + min-width: 0; + min-height: auto; + padding: 0; + border-radius: 100rem; + vertical-align: baseline +} + +.animation-blink { + -webkit-animation: blink 2s infinite; + animation: blink 2s infinite +} + +@keyframes blink { + 0% { + opacity: 0 + } + + 50% { + opacity: 1 + } + + to { + opacity: 0 + } } \ No newline at end of file diff --git a/resources/views/layouts/admin.blade.php b/resources/views/layouts/admin.blade.php index e44b5c4..e4a4182 100644 --- a/resources/views/layouts/admin.blade.php +++ b/resources/views/layouts/admin.blade.php @@ -27,11 +27,21 @@ Logo -
- @isset($title) - {{ $title }} - @endisset +