diff --git a/app/Http/Controllers/Admin/BasicSettings/DiscordNotificationController.php b/app/Http/Controllers/Admin/BasicSettings/DiscordNotificationController.php
index 7441543..213eae1 100644
--- a/app/Http/Controllers/Admin/BasicSettings/DiscordNotificationController.php
+++ b/app/Http/Controllers/Admin/BasicSettings/DiscordNotificationController.php
@@ -128,16 +128,37 @@ class DiscordNotificationController extends AdminController
$request->validate([
'discord_webhook_url' => 'nullable|url|max:500',
- 'temp_alert_enabled' => 'required|string|in:inherit,enabled,disabled',
+ 'notify_types_mode' => 'required|string|in:inherit,custom',
+ 'discord_notify_types' => 'nullable|array',
+ 'discord_notify_types.*' => 'string|in:submachine,status,temperature',
'temp_upper_limit' => 'nullable|numeric|min:-50|max:100',
'temp_lower_limit' => 'nullable|numeric|min:-50|max:100',
]);
$settings = $machine->settings ?? [];
$settings['discord_webhook_url'] = $request->input('discord_webhook_url');
- $settings['temp_alert_enabled'] = $request->input('temp_alert_enabled');
- $settings['temp_upper_limit'] = $request->filled('temp_upper_limit') ? (float)$request->temp_upper_limit : null;
- $settings['temp_lower_limit'] = $request->filled('temp_lower_limit') ? (float)$request->temp_lower_limit : null;
+
+ $notifyTypesMode = $request->input('notify_types_mode', 'inherit');
+ $notifyTypes = $request->input('discord_notify_types', []);
+ $hasTemperature = in_array('temperature', $notifyTypes, true);
+
+ if ($notifyTypesMode === 'custom') {
+ $settings['discord_notify_types'] = array_values($notifyTypes);
+ if ($hasTemperature) {
+ $settings['temp_alert_enabled'] = 'enabled';
+ $settings['temp_upper_limit'] = $request->filled('temp_upper_limit') ? (float)$request->temp_upper_limit : null;
+ $settings['temp_lower_limit'] = $request->filled('temp_lower_limit') ? (float)$request->temp_lower_limit : null;
+ } else {
+ $settings['temp_alert_enabled'] = 'disabled';
+ $settings['temp_upper_limit'] = null;
+ $settings['temp_lower_limit'] = null;
+ }
+ } else {
+ unset($settings['discord_notify_types']);
+ $settings['temp_alert_enabled'] = 'inherit';
+ $settings['temp_upper_limit'] = null;
+ $settings['temp_lower_limit'] = null;
+ }
$machine->settings = $settings;
$machine->save();
diff --git a/app/Jobs/Machine/ProcessHeartbeat.php b/app/Jobs/Machine/ProcessHeartbeat.php
index f196d9d..b4fac1e 100644
--- a/app/Jobs/Machine/ProcessHeartbeat.php
+++ b/app/Jobs/Machine/ProcessHeartbeat.php
@@ -178,16 +178,8 @@ class ProcessHeartbeat implements ShouldQueue
$activeStatus = \Illuminate\Support\Facades\Cache::get($activeKey);
if ($temp > $upperLimit) {
- // 若從低溫直接跳到高溫,先恢復低溫
+ // 若從低溫直接跳到高溫,清除低溫告警冷卻快取,但不發送恢復通知
if ($activeStatus === 'low') {
- \App\Jobs\Machine\ProcessStateLog::dispatch(
- $machine->id,
- $machine->company_id,
- "Temperature restored: :temp°C",
- 'info',
- ['temp' => $temp],
- 'temperature'
- );
\Illuminate\Support\Facades\Cache::forget($cooldownKey);
}
@@ -206,16 +198,8 @@ class ProcessHeartbeat implements ShouldQueue
\Illuminate\Support\Facades\Cache::put($activeKey, 'high', 86400);
}
} elseif ($temp < $lowerLimit) {
- // 若從高溫直接跳到低溫,先恢復高溫
+ // 若從高溫直接跳到低溫,清除高溫告警冷卻快取,但不發送恢復通知
if ($activeStatus === 'high') {
- \App\Jobs\Machine\ProcessStateLog::dispatch(
- $machine->id,
- $machine->company_id,
- "Temperature restored: :temp°C",
- 'info',
- ['temp' => $temp],
- 'temperature'
- );
\Illuminate\Support\Facades\Cache::forget($cooldownKey);
}
diff --git a/lang/en.json b/lang/en.json
index 7b3c227..64c203b 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -2131,5 +2131,11 @@
"Default Temp Alert Upper Limit (°C)": "Default Temp Alert Upper Limit (°C)",
"Default Temp Alert Lower Limit (°C)": "Default Temp Alert Lower Limit (°C)",
"Default Temp Alert Limits": "Default Temp Alert Limits",
+ "Alert Types Source": "Alert Types Source",
+ "Inherit Company Alert Types": "Inherit Company Alert Types",
+ "Custom Alert Types": "Custom Alert Types",
+ "Effective Alert Types": "Effective Alert Types",
+ "Machine Alert Settings": "Machine Alert Settings",
+ "Configure alert types and temperature thresholds": "Configure alert types and temperature thresholds",
"Machine model saved successfully.": "Machine model saved successfully."
-}
\ No newline at end of file
+}
diff --git a/lang/ja.json b/lang/ja.json
index b8f87c1..986fcbf 100644
--- a/lang/ja.json
+++ b/lang/ja.json
@@ -2134,5 +2134,11 @@
"Default Temp Alert Upper Limit (°C)": "デフォルト温度アラート上限 (°C)",
"Default Temp Alert Lower Limit (°C)": "デフォルト温度アラート下限 (°C)",
"Default Temp Alert Limits": "デフォルト温度アラート範囲",
+ "Alert Types Source": "アラート種別ソース",
+ "Inherit Company Alert Types": "会社のアラート種別を継承",
+ "Custom Alert Types": "カスタムアラート種別",
+ "Effective Alert Types": "有効なアラート種別",
+ "Machine Alert Settings": "機台アラート設定",
+ "Configure alert types and temperature thresholds": "アラート種別と温度しきい値を設定",
"Machine model saved successfully.": "機器モデルが正常に追加されました。"
-}
\ No newline at end of file
+}
diff --git a/lang/zh_TW.json b/lang/zh_TW.json
index 5d9639b..3170218 100644
--- a/lang/zh_TW.json
+++ b/lang/zh_TW.json
@@ -2190,5 +2190,11 @@
"Default Temp Alert Upper Limit (°C)": "預設溫度告警上限 (°C)",
"Default Temp Alert Lower Limit (°C)": "預設溫度告警下限 (°C)",
"Default Temp Alert Limits": "預設溫度告警範圍",
+ "Alert Types Source": "告警類型來源",
+ "Inherit Company Alert Types": "繼承公司告警類型",
+ "Custom Alert Types": "自訂告警類型",
+ "Effective Alert Types": "目前生效的告警類型",
+ "Machine Alert Settings": "機台告警設定",
+ "Configure alert types and temperature thresholds": "設定告警類型與溫度門檻",
"Machine model saved successfully.": "機台型號已成功新增。"
-}
\ No newline at end of file
+}
diff --git a/resources/views/admin/basic-settings/discord-notifications/index.blade.php b/resources/views/admin/basic-settings/discord-notifications/index.blade.php
index 2c02d00..f1e54b0 100644
--- a/resources/views/admin/basic-settings/discord-notifications/index.blade.php
+++ b/resources/views/admin/basic-settings/discord-notifications/index.blade.php
@@ -262,7 +262,8 @@
{{ __('Company') }}
@endif
{{ __('Webhook Status') }}
- {{ __('Temp Alert State') }}
+ {{ __('Configured Alert Types') }}
+ {{ __('Temp Alert Range') }}
{{ __('Actions') }}
@@ -273,6 +274,11 @@
$mWebhook = $mSettings['discord_webhook_url'] ?? null;
$cSettings = $machine->company->settings ?? [];
$cWebhook = $cSettings['discord_webhook_url'] ?? null;
+ $cNotifyTypes = $cSettings['discord_notify_types'] ?? [];
+ $mNotifyTypes = array_key_exists('discord_notify_types', $mSettings) && is_array($mSettings['discord_notify_types']) ? $mSettings['discord_notify_types'] : null;
+ $effectiveNotifyTypes = $mNotifyTypes ?? $cNotifyTypes;
+ $isTypeInherited = $mNotifyTypes === null;
+ $hasTemperatureNotify = in_array('temperature', $effectiveNotifyTypes, true);
$mTempAlert = $mSettings['temp_alert_enabled'] ?? 'inherit';
@@ -291,11 +297,11 @@
$finalLower = $sysLower;
$tempSource = __('System Default');
- if ($mTempAlert === 'enabled' && ($mUpper !== null || $mLower !== null)) {
+ if ($mTempAlert === 'enabled' && $hasTemperatureNotify && ($mUpper !== null || $mLower !== null)) {
$finalUpper = $mUpper !== null ? $mUpper : $sysUpper;
$finalLower = $mLower !== null ? $mLower : $sysLower;
$tempSource = __('Custom');
- } elseif ($modelUpper !== null || $modelLower !== null) {
+ } elseif ($hasTemperatureNotify && ($modelUpper !== null || $modelLower !== null)) {
$finalUpper = $modelUpper !== null ? $modelUpper : $sysUpper;
$finalLower = $modelLower !== null ? $modelLower : $sysLower;
$tempSource = __('Model Default');
@@ -331,36 +337,48 @@
{{ __('Machine Specific') }}
@elseif($cWebhook)
{{ Str::limit($cWebhook, 30) }}
- {{ __('Inherit Company') }}
+ {{ __('Inherit Company') }}
@else
{{ __('Not Configured') }}
@endif
-
-
- @if($mTempAlert === 'enabled')
-
- @elseif($mTempAlert === 'disabled')
-
- @else
-
- {{ __('Inherit') }}
-
+
+ @if($isTypeInherited)
+
{{ __('Inherit Company') }}
+ @endif
+
+ @foreach($effectiveNotifyTypes as $type)
+ @php
+ $label = match($type) {
+ 'submachine' => __('Device Exception'),
+ 'status' => __('Connectivity Logs'),
+ 'temperature' => __('Temperature Alert'),
+ default => $type
+ };
+ @endphp
+ {{ $label }}
+ @endforeach
+ @if(empty($effectiveNotifyTypes))
+ {{ __('Not Configured') }}
@endif
- @if($mTempAlert !== 'disabled')
-
-
- {{ number_format($finalLower, 0) }}°C ~ {{ number_format($finalUpper, 0) }}°C
-
-
- ({{ $tempSource }})
-
-
- @endif
+
+ @if($mTempAlert !== 'disabled' && $hasTemperatureNotify)
+
+
+ {{ number_format($finalLower, 0) }}°C ~ {{ number_format($finalUpper, 0) }}°C
+
+
+ ({{ $tempSource }})
+
+
+ @else
+ —
+ @endif
+
@@ -369,7 +387,7 @@
@empty
-
+
@endforelse
@@ -383,6 +401,11 @@
$mWebhook = $mSettings['discord_webhook_url'] ?? null;
$cSettings = $machine->company->settings ?? [];
$cWebhook = $cSettings['discord_webhook_url'] ?? null;
+ $cNotifyTypes = $cSettings['discord_notify_types'] ?? [];
+ $mNotifyTypes = array_key_exists('discord_notify_types', $mSettings) && is_array($mSettings['discord_notify_types']) ? $mSettings['discord_notify_types'] : null;
+ $effectiveNotifyTypes = $mNotifyTypes ?? $cNotifyTypes;
+ $isTypeInherited = $mNotifyTypes === null;
+ $hasTemperatureNotify = in_array('temperature', $effectiveNotifyTypes, true);
$mTempAlert = $mSettings['temp_alert_enabled'] ?? 'inherit';
@@ -401,11 +424,11 @@
$finalLower = $sysLower;
$tempSource = __('System Default');
- if ($mTempAlert === 'enabled' && ($mUpper !== null || $mLower !== null)) {
+ if ($mTempAlert === 'enabled' && $hasTemperatureNotify && ($mUpper !== null || $mLower !== null)) {
$finalUpper = $mUpper !== null ? $mUpper : $sysUpper;
$finalLower = $mLower !== null ? $mLower : $sysLower;
$tempSource = __('Custom');
- } elseif ($modelUpper !== null || $modelLower !== null) {
+ } elseif ($hasTemperatureNotify && ($modelUpper !== null || $modelLower !== null)) {
$finalUpper = $modelUpper !== null ? $modelUpper : $sysUpper;
$finalLower = $modelLower !== null ? $modelLower : $sysLower;
$tempSource = __('Model Default');
@@ -422,15 +445,7 @@
{{ $machine->serial_no }}
- @if($mTempAlert === 'enabled')
-
- @elseif($mTempAlert === 'disabled')
-
- @else
-
- {{ __('Inherit') }}
-
- @endif
+ {{-- Temperature status is implied by configured alert types; show only range below. --}}
@@ -451,12 +466,34 @@
{{ __('Machine Specific') }}
@elseif($cWebhook)
{{ $cWebhook }}
-
{{ __('Inherit Company') }}
+
{{ __('Inherit Company') }}
@else
{{ __('Not Configured') }}
@endif
- @if($mTempAlert !== 'disabled')
+
+
{{ __('Configured Alert Types') }}
+ @if($isTypeInherited)
+
{{ __('Inherit Company') }}
+ @endif
+
+ @foreach($effectiveNotifyTypes as $type)
+ @php
+ $label = match($type) {
+ 'submachine' => __('Device Exception'),
+ 'status' => __('Connectivity Logs'),
+ 'temperature' => __('Temperature Alert'),
+ default => $type
+ };
+ @endphp
+
{{ $label }}
+ @endforeach
+ @if(empty($effectiveNotifyTypes))
+
{{ __('Not Configured') }}
+ @endif
+
+
+ @if($mTempAlert !== 'disabled' && $hasTemperatureNotify)
{{ __('Temp Alert Range') }}
@@ -624,19 +661,34 @@
@@ -713,7 +765,7 @@
@csrf
@method('PUT')
-
+
@@ -793,33 +845,74 @@
-
+
+
-
-
{{ __('Define custom temperature limits or inherit from model') }}
+
+
{{ __('Configure alert types and temperature thresholds') }}
-
- {{ __('Alert Switch') }}
-
- {{ __('Inherit Model / System default') }}
- {{ __('Custom Enabled') }}
- {{ __('Custom Disabled (Mute Alert)') }}
+ {{ __('Alert Types Source') }}
+
+ {{ __('Inherit Company Alert Types') }}
+ {{ __('Custom Alert Types') }}
+
+
{{ __('Alert Types') }}
+
+
+
+ {{ __('Submachine Alert') }}
+
+
+
+
+
+
+
+ {{ __('Machine Connection Alert') }}
+
+
+
+
+
+
+
+ {{ __('Temperature Alert') }}
+
+
+
+
+
+
+
+
+
+
{{ __('Effective Alert Types') }}
+
+
+
+
+
+
+
-
+ class="grid grid-cols-1 sm:grid-cols-2 gap-4 animate-luxury-in">
{{ __('Custom Upper Limit (°C)') }}
@@ -903,6 +996,8 @@
},
machineForm: {
discord_webhook_url: '',
+ notify_types_mode: 'inherit',
+ discord_notify_types: [],
temp_alert_enabled: 'inherit',
temp_upper_limit: '',
temp_lower_limit: ''
@@ -936,23 +1031,63 @@
openMachineEdit(machine) {
const settings = machine.settings || {};
+ const machineNotifyTypes = Array.isArray(settings.discord_notify_types) ? settings.discord_notify_types : null;
this.currentMachine = machine;
this.machineForm.discord_webhook_url = settings.discord_webhook_url || '';
- this.machineForm.temp_alert_enabled = settings.temp_alert_enabled || 'inherit';
+ this.machineForm.notify_types_mode = machineNotifyTypes === null ? 'inherit' : 'custom';
+ this.machineForm.discord_notify_types = machineNotifyTypes === null ? [] : [...machineNotifyTypes];
+ this.machineForm.temp_alert_enabled = settings.temp_alert_enabled || (this.isTemperatureChecked() ? 'enabled' : 'disabled');
this.machineForm.temp_upper_limit = settings.temp_upper_limit !== undefined && settings.temp_upper_limit !== null ? Math.round(parseFloat(settings.temp_upper_limit)).toString() : '';
this.machineForm.temp_lower_limit = settings.temp_lower_limit !== undefined && settings.temp_lower_limit !== null ? Math.round(parseFloat(settings.temp_lower_limit)).toString() : '';
+ this.syncTempAlertState();
this.testStatus = null;
this.testMessage = '';
this.showMachineModal = true;
this.$nextTick(() => {
- const alertSel = window.HSSelect?.getInstance('#machine-alert-select');
- if (alertSel) alertSel.setValue(this.machineForm.temp_alert_enabled);
+ const modeSel = window.HSSelect?.getInstance('#machine-notify-mode-select');
+ if (modeSel) modeSel.setValue(this.machineForm.notify_types_mode);
this.rebuildCopySelect();
});
},
+ notifyTypeLabel(type) {
+ if (type === 'submachine') return '{{ __('Submachine Alert') }}';
+ if (type === 'status') return '{{ __('Machine Connection Alert') }}';
+ if (type === 'temperature') return '{{ __('Temperature Alert') }}';
+ return type;
+ },
+
+ effectiveNotifyTypes() {
+ if (this.machineForm.notify_types_mode === 'custom') {
+ return this.machineForm.discord_notify_types;
+ }
+ return this.currentMachine?.company?.settings?.discord_notify_types || [];
+ },
+
+ isTemperatureChecked() {
+ return this.effectiveNotifyTypes().includes('temperature');
+ },
+
+ syncTempAlertState() {
+ if (this.machineForm.notify_types_mode === 'inherit') {
+ this.machineForm.temp_alert_enabled = 'inherit';
+ this.machineForm.temp_upper_limit = '';
+ this.machineForm.temp_lower_limit = '';
+ return;
+ }
+
+ if (this.machineForm.discord_notify_types.includes('temperature')) {
+ this.machineForm.temp_alert_enabled = 'enabled';
+ return;
+ }
+
+ this.machineForm.temp_alert_enabled = 'disabled';
+ this.machineForm.temp_upper_limit = '';
+ this.machineForm.temp_lower_limit = '';
+ },
+
rebuildCopySelect() {
this.$nextTick(() => {
const wrapper = document.getElementById('copy-select-wrapper');
@@ -1039,18 +1174,28 @@
},
handleCopyMachine(value) {
- if (!value) return;
+ if (!value || !String(value).trim()) return;
const source = this.copyableMachines.find(m => m.id == value);
if (source) {
const s = source.settings || {};
+ const sourceNotifyTypes = Array.isArray(s.discord_notify_types) ? s.discord_notify_types : null;
this.machineForm.discord_webhook_url = s.discord_webhook_url || '';
- this.machineForm.temp_alert_enabled = s.temp_alert_enabled || 'inherit';
+ this.machineForm.notify_types_mode = sourceNotifyTypes === null ? 'inherit' : 'custom';
+ this.machineForm.discord_notify_types = sourceNotifyTypes === null ? [] : [...sourceNotifyTypes];
+ this.machineForm.temp_alert_enabled = s.temp_alert_enabled || (this.isTemperatureChecked() ? 'enabled' : 'disabled');
this.machineForm.temp_upper_limit = s.temp_upper_limit !== undefined && s.temp_upper_limit !== null ? Math.round(parseFloat(s.temp_upper_limit)).toString() : '';
this.machineForm.temp_lower_limit = s.temp_lower_limit !== undefined && s.temp_lower_limit !== null ? Math.round(parseFloat(s.temp_lower_limit)).toString() : '';
+ this.syncTempAlertState();
this.$nextTick(() => {
- const el = window.HSSelect?.getInstance('#machine-alert-select');
- if (el) el.setValue(this.machineForm.temp_alert_enabled);
+ const modeSel = window.HSSelect?.getInstance('#machine-notify-mode-select');
+ if (modeSel) modeSel.setValue(this.machineForm.notify_types_mode);
+ const copyEl = document.getElementById('copy-machine-select');
+ if (copyEl) {
+ copyEl.value = ' ';
+ const copyInstance = window.HSSelect?.getInstance('#copy-machine-select');
+ if (copyInstance) copyInstance.setValue(' ');
+ }
});
this.copiedMachineName = source.name;