Add notification preferences and admin governance
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
#[Fillable(['actor_id', 'action', 'metadata', 'ip_address', 'user_agent'])]
|
||||
class AuditLog extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'metadata' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
public function actor(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'actor_id');
|
||||
}
|
||||
|
||||
public function auditable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public static function record(Request $request, string $action, ?Model $auditable = null, array $metadata = []): self
|
||||
{
|
||||
$log = new self([
|
||||
'actor_id' => $request->user()?->id,
|
||||
'action' => $action,
|
||||
'metadata' => $metadata,
|
||||
'ip_address' => $request->ip(),
|
||||
'user_agent' => substr((string) $request->userAgent(), 0, 500),
|
||||
]);
|
||||
|
||||
if ($auditable) {
|
||||
$log->auditable()->associate($auditable);
|
||||
}
|
||||
|
||||
$log->save();
|
||||
|
||||
return $log;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
#[Fillable([
|
||||
'reporter_id',
|
||||
'reason_code',
|
||||
'details',
|
||||
'status',
|
||||
'moderated_by_id',
|
||||
'moderated_at',
|
||||
'moderation_notes',
|
||||
])]
|
||||
class ModerationReport extends Model
|
||||
{
|
||||
public const REASON_CODES = [
|
||||
'spam' => 'Spam or solicitation',
|
||||
'misleading' => 'Misleading or false information',
|
||||
'private_info' => 'Private or sensitive information',
|
||||
'abuse' => 'Abuse or harassment',
|
||||
'unsafe' => 'Unsafe work or conduct',
|
||||
'other' => 'Other',
|
||||
];
|
||||
|
||||
public const STATUSES = ['open', 'reviewing', 'action_taken', 'dismissed'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'moderated_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function reporter(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reporter_id');
|
||||
}
|
||||
|
||||
public function moderatedBy(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'moderated_by_id');
|
||||
}
|
||||
|
||||
public function reportable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,37 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[Fillable([
|
||||
'user_id', 'job_alerts', 'quote_updates', 'work_order_updates',
|
||||
'review_updates', 'dispute_updates', 'email_enabled', 'push_enabled',
|
||||
'review_updates', 'dispute_updates', 'in_app_enabled', 'email_enabled', 'push_enabled',
|
||||
'event_preferences', 'digest_frequency', 'quiet_hours_start', 'quiet_hours_end',
|
||||
])]
|
||||
class NotificationPreference extends Model
|
||||
{
|
||||
public const EVENT_CATEGORIES = [
|
||||
'job_alerts' => 'Matching jobs',
|
||||
'quote_updates' => 'Quote updates',
|
||||
'work_order_updates' => 'Work-order activity',
|
||||
'review_updates' => 'Reviews and reputation',
|
||||
'dispute_updates' => 'Disputes and moderation',
|
||||
];
|
||||
|
||||
public const EVENT_TYPES = [
|
||||
'new_matching_job' => 'job_alerts',
|
||||
'quote_submitted' => 'quote_updates',
|
||||
'quote_revised' => 'quote_updates',
|
||||
'quote_declined' => 'quote_updates',
|
||||
'quote_accepted' => 'quote_updates',
|
||||
'work_order_status' => 'work_order_updates',
|
||||
'work_order_change_request' => 'work_order_updates',
|
||||
'work_order_message' => 'work_order_updates',
|
||||
'contact_event' => 'work_order_updates',
|
||||
'running_late' => 'work_order_updates',
|
||||
'schedule_update_requested' => 'work_order_updates',
|
||||
'provider_tag_verification' => 'review_updates',
|
||||
'review_received' => 'review_updates',
|
||||
'dispute_opened' => 'dispute_updates',
|
||||
'moderation_action' => 'dispute_updates',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
@@ -20,8 +47,10 @@ class NotificationPreference extends Model
|
||||
'work_order_updates' => 'boolean',
|
||||
'review_updates' => 'boolean',
|
||||
'dispute_updates' => 'boolean',
|
||||
'in_app_enabled' => 'boolean',
|
||||
'email_enabled' => 'boolean',
|
||||
'push_enabled' => 'boolean',
|
||||
'event_preferences' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -29,4 +58,28 @@ class NotificationPreference extends Model
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function allows(string $eventType, string $channel = 'database'): bool
|
||||
{
|
||||
if ($channel === 'database' && $this->in_app_enabled === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($channel === 'mail' && ! $this->email_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($channel === 'push' && ! $this->push_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$eventPreferences = $this->event_preferences ?? [];
|
||||
if (array_key_exists($eventType, $eventPreferences)) {
|
||||
return (bool) $eventPreferences[$eventType];
|
||||
}
|
||||
|
||||
$category = self::EVENT_TYPES[$eventType] ?? null;
|
||||
|
||||
return $category ? $this->{$category} !== false : true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user