Initial Commit

This commit is contained in:
Mark Gregory
2026-09-29 11:12:07 +01:00
commit 89bbdbfcba
133 changed files with 15743 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
@extends('layouts.app')
@section('content')
<section class="portal-header"><div><span class="eyebrow">CMS</span><h1>{{ $page->exists ? 'Edit page' : 'New page' }}</h1></div><a class="btn btn-secondary" href="{{ route('admin.cms.index') }}">Back to pages</a></section>
<div class="panel"><form method="post" action="{{ $page->exists ? route('admin.cms.update',$page) : route('admin.cms.store') }}">@csrf @if($page->exists) @method('PUT') @endif
<label>Title<input name="title" value="{{ old('title',$page->title) }}" required maxlength="160"></label>
<label>Slug<input name="slug" value="{{ old('slug',$page->slug) }}" placeholder="generated-from-title" maxlength="180"></label>
<label>Excerpt<textarea name="excerpt" rows="3" maxlength="1000">{{ old('excerpt',$page->excerpt) }}</textarea></label>
<label>Body<textarea name="body" rows="18" required>{{ old('body',$page->body) }}</textarea><small>Plain text is rendered safely with paragraphs and line breaks.</small></label>
<div class="form-grid two"><label>SEO title<input name="meta_title" value="{{ old('meta_title',$page->meta_title) }}" maxlength="160"></label><label>SEO description<textarea name="meta_description" rows="3" maxlength="320">{{ old('meta_description',$page->meta_description) }}</textarea></label></div>
<label class="consent-check"><input type="checkbox" name="is_published" value="1" @checked(old('is_published',$page->is_published))><span>Published</span></label>
<div class="support-actions"><button type="submit">Save page</button>@if($page->exists && $page->is_published)<a class="btn btn-secondary" target="_blank" href="{{ route('cms.show',$page->slug) }}">View page</a>@endif</div>
</form></div>
@if($page->exists)<form method="post" action="{{ route('admin.cms.destroy',$page) }}" style="margin-top:16px" onsubmit="return confirm('Delete this page?')">@csrf @method('DELETE')<button class="danger-outline">Delete page</button></form>@endif
@endsection
@@ -0,0 +1,9 @@
@extends('layouts.app')
@section('content')
<section class="portal-header"><div><span class="eyebrow">CMS</span><h1>Content pages</h1><p class="lead compact">Manage SonarDesk website content.</p></div><a class="btn" href="{{ route('admin.cms.create') }}">New page</a></section>
<div class="panel"><div class="admin-ticket-list">
@forelse($pages as $page)
<a class="admin-ticket-row" href="{{ route('admin.cms.edit', $page) }}"><div><strong>{{ $page->title }}</strong><small>/pages/{{ $page->slug }}</small></div><span class="ticket-status {{ $page->is_published ? 'status-open' : '' }}">{{ $page->is_published ? 'Published' : 'Draft' }}</span><time>{{ $page->updated_at->diffForHumans() }}</time><span>›</span></a>
@empty <p class="muted">No CMS pages yet.</p> @endforelse
</div>{{ $pages->links() }}</div>
@endsection
+6
View File
@@ -0,0 +1,6 @@
@extends('layouts.app')
@section('content')
<section class="portal-header"><div><span class="eyebrow">Admin</span><h1>Licensing administration</h1><p class="lead compact">Manage licences, activations and customer support.</p></div></section>
<div class="grid"><div class="card"><strong>{{ $licenseCount }}</strong><p>Licences</p></div><div class="card"><strong>{{ $activeCount }}</strong><p>Not revoked</p></div><div class="card"><strong>{{ $activationCount }}</strong><p>Active installations</p></div><div class="card"><strong>{{ $userCount }}</strong><p>Portal users</p></div></div>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:18px"><a class="btn" href="{{ route('admin.licenses') }}">Manage licences</a><a class="btn btn-secondary" href="{{ route('admin.cms.index') }}">Manage content</a><a class="btn btn-secondary" href="{{ route('admin.support.index') }}">Support inbox</a></div>
@endsection
+1
View File
@@ -0,0 +1 @@
@extends('layouts.app') @section('content')<div class="card"><h1>Licence •••• {{ $license->license_key_last4 }}</h1><p>{{ $license->customer_name }} · {{ $license->customer_email }}</p><p>{{ $license->edition }} · {{ $license->activation_limit }} seats</p>@if($license->revoked_at)<form method="post" action="{{ route('admin.restore',$license) }}">@csrf<button>Restore licence</button></form>@else<form method="post" action="{{ route('admin.revoke',$license) }}">@csrf<label>Reason</label><input name="reason"><button class="danger">Revoke licence</button></form>@endif</div><div class="card"><h2>Activations</h2><table><tr><th>ID</th><th>Platform</th><th>Version</th><th>Status</th></tr>@foreach($license->activations as $a)<tr><td>{{ $a->installation_name ?: $a->installation_id }}</td><td>{{ $a->platform }}</td><td>{{ $a->app_version }}</td><td>{{ $a->deactivated_at ? 'Released':'Active' }}</td></tr>@endforeach</table></div>@endsection
+1
View File
@@ -0,0 +1 @@
@extends('layouts.app') @section('content')<h1>Licences</h1><form><input name="q" value="{{ request('q') }}" placeholder="Search email, name or last 4"><button>Search</button></form><div class="card"><table><tr><th>Customer</th><th>Edition</th><th>Key</th><th>Status</th></tr>@foreach($licenses as $l)<tr><td><a href="{{ route('admin.license',$l) }}">{{ $l->customer_email ?: 'Manual' }}</a></td><td>{{ $l->edition }}</td><td>•••• {{ $l->license_key_last4 }}</td><td>{{ $l->revoked_at ? 'Revoked':'Active' }}</td></tr>@endforeach</table>{{ $licenses->links() }}</div>@endsection
+24
View File
@@ -0,0 +1,24 @@
@extends('layouts.app', ['title' => 'Support Inbox — SonarDesk'])
@section('content')
<section class="portal-header"><div><span class="eyebrow">Admin</span><h1>Support inbox</h1><p class="lead compact">Customer, licence, billing and privacy requests.</p></div></section>
<form method="get" class="filter-bar">
<label for="status">Status</label>
<select id="status" name="status" onchange="this.form.submit()">
<option value="">All</option>
@foreach(['new','open','waiting','resolved','closed'] as $status)<option value="{{ $status }}" @selected(request('status') === $status)>{{ ucfirst($status) }}</option>@endforeach
</select>
</form>
<div class="admin-ticket-list">
@forelse($tickets as $ticket)
<a class="admin-ticket-row" href="{{ route('admin.support.show', $ticket) }}">
<div><span class="ticket-ref">{{ $ticket->reference }}</span><strong>{{ $ticket->subject }}</strong><small>{{ $ticket->name }} · {{ $ticket->email }}</small></div>
<span class="ticket-category">{{ ucfirst($ticket->category) }}</span>
<span class="ticket-status status-{{ $ticket->status }}">{{ ucfirst($ticket->status) }}</span>
<time>{{ $ticket->created_at->diffForHumans() }}</time>
</a>
@empty
<div class="empty-state"><h2>No support requests</h2><p>The support inbox is empty.</p></div>
@endforelse
</div>
{{ $tickets->links() }}
@endsection
+14
View File
@@ -0,0 +1,14 @@
@extends('layouts.app', ['title' => $ticket->reference.' — Support'])
@section('content')
<a class="back-link" href="{{ route('admin.support.index') }}">← Support inbox</a>
<section class="ticket-detail-head"><div><span class="ticket-ref">{{ $ticket->reference }}</span><h1>{{ $ticket->subject }}</h1><p>{{ $ticket->name }} · <a href="mailto:{{ $ticket->email }}?subject=Re:%20{{ rawurlencode($ticket->reference.' '.$ticket->subject) }}">{{ $ticket->email }}</a></p></div><span class="ticket-status status-{{ $ticket->status }}">{{ ucfirst($ticket->status) }}</span></section>
<div class="detail-grid">
<article class="panel ticket-message"><h2>Request</h2><div class="ticket-meta"><span>{{ ucfirst($ticket->category) }}</span><span>{{ $ticket->created_at->format('j M Y H:i') }}</span></div><div class="message-body">{!! nl2br(e($ticket->message)) !!}</div><a class="btn btn-primary" href="mailto:{{ $ticket->email }}?subject=Re:%20{{ rawurlencode($ticket->reference.' '.$ticket->subject) }}">Reply by email</a></article>
<aside class="panel"><h2>Manage</h2><form method="post" action="{{ route('admin.support.update', $ticket) }}">@csrf @method('PATCH')
<label>Status<select name="status">@foreach(['new','open','waiting','resolved','closed'] as $status)<option value="{{ $status }}" @selected($ticket->status === $status)>{{ ucfirst($status) }}</option>@endforeach</select></label>
<label>Priority<select name="priority">@foreach(['low','normal','high','urgent'] as $priority)<option value="{{ $priority }}" @selected($ticket->priority === $priority)>{{ ucfirst($priority) }}</option>@endforeach</select></label>
<label>Assigned to<select name="assigned_to"><option value="">Unassigned</option>@foreach($admins as $admin)<option value="{{ $admin->id }}" @selected($ticket->assigned_to === $admin->id)>{{ $admin->name }} — {{ $admin->email }}</option>@endforeach</select></label>
<button class="full-width">Save changes</button>
</form></aside>
</div>
@endsection