Files
autocrm/app/Services/Notifications/ShopOrderNotifier.php
T
Vasyka 3da1f5412a feat: shop UX polish — password reset / order email / multi-image / customer admin
Shop password reset:
- Configured 'shop_customers' password broker on the existing
  password_reset_tokens table
- ShopCustomer::sendPasswordResetNotification overrides Laravel default to
  send a ShopPasswordResetMail with a tenant-subdomain reset URL
- Routes /shop/password/forgot, /shop/password/email, /shop/password/reset/{token}
  + ShopAuthController showForgotPassword/sendResetLink/showResetPassword/
  resetPassword. Forgot view stays generic ("if it exists, we sent…") to avoid
  email enumeration. Login view links to "Am uitat parola".

Order confirmation email:
- ShopOrderConfirmationMail + nicely formatted HTML email template
- ShopOrderNotifier::placed now also emails customer_email (best-effort,
  warning-only logged on failure) alongside existing Telegram + staff push

Multiple images per Part:
- Part media collection switched from singleFile to multiple (max 8 in form)
- imageUrls() helper for galleries; imageUrl() still returns first for cards
- PartResource form: reorderable multi-upload
- Shop part detail: vertical thumbnails switch the main image via vanilla JS

ShopCustomerResource (tenant Filament, "Magazin" nav group):
- List with name/phone/email/client_id/orders_count/last_login_at
- Edit (no password field exposed)
- "Trimite reset parolă" action uses the new broker
- OrdersRelationManager shows the customer's orders read-only

Tests (7 new):
- forgot sends mail; forgot doesn't disclose unknown email; reset with valid
  token changes password; bad token rejected; order email when customer_email
  set; email skipped without it; Part has imageUrls() collection

Full suite: 130 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-03 06:14:45 +00:00

71 lines
2.8 KiB
PHP

<?php
namespace App\Services\Notifications;
use App\Models\Central\Company;
use App\Models\Tenant\Client;
use App\Models\Tenant\OnlineOrder;
use App\Models\Tenant\User;
/**
* Notifies staff of a new online order (Web Push) and confirms to the customer
* via Telegram if they have a linked chat. Best-effort — never throws.
*/
class ShopOrderNotifier
{
public function __construct(
private WebPushService $push,
private TelegramService $telegram,
) {
}
public function placed(OnlineOrder $order): void
{
$company = Company::withoutGlobalScopes()->find($order->company_id);
if (! $company) return;
// ── Staff: Web Push to active users of this tenant ──
$title = 'Comandă nouă #' . $order->number;
$body = $order->customer_name . ' · ' . number_format((float) $order->total, 2) . ' '
. ($company->settings['currency'] ?? 'MDL');
$url = '/app/resources/online-orders/' . $order->id . '/edit';
$userIds = User::where('status', 'active')->pluck('id');
foreach ($userIds as $uid) {
$this->push->sendToUser((int) $uid, $title, $body, $url, 'shop-order-' . $order->id);
}
// ── Customer: Telegram if their phone is linked ──
$needle = Client::normalizePhone($order->customer_phone);
if ($needle) {
$client = Client::whereNotNull('telegram_chat_id')
->whereRaw(
"REPLACE(REPLACE(REPLACE(REPLACE(phone, ' ', ''), '-', ''), '(', ''), ')', '') LIKE ?",
['%' . substr($needle, -9) . '%']
)
->first();
if ($client && $client->telegram_chat_id) {
$brand = htmlspecialchars($company->display_name ?? $company->name);
$text = "🛒 <b>Comanda #{$order->number} primită</b>\n"
. "Total: <b>" . number_format((float) $order->total, 2) . " "
. ($company->settings['currency'] ?? 'MDL') . "</b>\n\n"
. "Urmărește statusul: " . $order->trackingUrl() . "\n\n{$brand}";
$this->telegram->sendMessage($company, (string) $client->telegram_chat_id, $text);
}
}
// ── Customer: email confirmation when address given ──
if ($order->customer_email) {
try {
\Illuminate\Support\Facades\Mail::to($order->customer_email)
->send(new \App\Mail\ShopOrderConfirmationMail($order, $company));
} catch (\Throwable $e) {
\Illuminate\Support\Facades\Log::warning('shop order confirmation mail failed', [
'order' => $order->id, 'err' => $e->getMessage(),
]);
}
}
}
}