updates
This commit is contained in:
Regular → Executable
+113
-1
@@ -1 +1,113 @@
|
||||
@extends('layouts.app') @section('content')<h1>My licences</h1>@forelse($licenses as $license)<div class="card"><h2>{{ $license->product->name }} · {{ $license->edition }}</h2><p>Licence ending <strong>{{ $license->license_key_last4 }}</strong> · {{ $license->activations->whereNull('deactivated_at')->count() }}/{{ $license->activation_limit }} activations</p><p class="{{ $license->is_usable ? 'ok':'bad' }}">{{ $license->is_usable ? 'Active':'Unavailable' }}</p><a class="btn" href="{{ route('portal.show',$license) }}">Manage</a></div>@empty<div class="card">No licences are associated with {{ auth()->user()->email }}.</div>@endforelse@endsection
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<section class="portal-header">
|
||||
<div>
|
||||
<span class="eyebrow">Account</span>
|
||||
<h1>My licences</h1>
|
||||
<p class="lead compact">Manage subscriptions, licence keys and activated Macs.</p>
|
||||
</div>
|
||||
<a class="btn btn-secondary" href="{{ route('buy') }}">Buy another licence</a>
|
||||
</section>
|
||||
|
||||
@php
|
||||
$activeLicences = $licenses->filter(fn ($license) => $license->is_usable);
|
||||
$historyLicences = $licenses->reject(fn ($license) => $license->is_usable);
|
||||
@endphp
|
||||
|
||||
@if($activeLicences->isEmpty())
|
||||
<div class="empty-state">
|
||||
<h2>No active licences</h2>
|
||||
<p>No active Rsync Studio licence is associated with {{ auth()->user()->email }}.</p>
|
||||
<a class="btn" href="{{ route('buy') }}">Choose a plan</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="licence-list">
|
||||
@foreach($activeLicences as $license)
|
||||
@php
|
||||
$activeCount = $license->activations->whereNull('deactivated_at')->count();
|
||||
$isDuplicatePersonal = $hasProfessional && $license->edition === 'Personal';
|
||||
$billing = str_contains(strtolower((string) optional($license->product)->code), 'annual') ? 'Annual' : 'Monthly';
|
||||
@endphp
|
||||
<article class="licence-card {{ $license->edition === 'Professional' ? 'licence-card-featured' : '' }}">
|
||||
<div class="licence-main">
|
||||
<div class="licence-title-row">
|
||||
<div>
|
||||
<div class="badge-row">
|
||||
<span class="status-badge status-active">Active</span>
|
||||
<span class="plan-badge">{{ $license->edition }}</span>
|
||||
<span class="billing-badge">{{ $billing }}</span>
|
||||
</div>
|
||||
<h2>Rsync Studio {{ $license->edition }}</h2>
|
||||
<p class="muted licence-key">Licence ending <strong>{{ $license->license_key_last4 }}</strong></p>
|
||||
</div>
|
||||
<a class="btn btn-secondary" href="{{ route('portal.show', $license) }}">Manage licence</a>
|
||||
</div>
|
||||
|
||||
<div class="licence-stats">
|
||||
<div>
|
||||
<span class="stat-label">Macs activated</span>
|
||||
<strong>{{ $activeCount }} of {{ $license->activation_limit }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span class="stat-label">Subscription</span>
|
||||
<strong>{{ ucfirst($license->subscription_status ?: 'active') }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span class="stat-label">{{ $license->subscription_status === 'trialing' ? 'Trial ends' : 'Renews' }}</span>
|
||||
<strong>{{ optional($license->current_period_ends_at)->format('j M Y') ?: '—' }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="activation-meter" aria-label="{{ $activeCount }} of {{ $license->activation_limit }} Macs activated">
|
||||
<span style="width: {{ min(100, ($activeCount / max(1, $license->activation_limit)) * 100) }}%"></span>
|
||||
</div>
|
||||
|
||||
@if($isDuplicatePersonal)
|
||||
<div class="subscription-warning">
|
||||
<div>
|
||||
<strong>You already have an active Professional subscription.</strong>
|
||||
<p>This Personal plan is a separate Paddle subscription and could be billed separately. Cancel it if it was only created while upgrading.</p>
|
||||
</div>
|
||||
<form method="post" action="{{ route('portal.cancel', $license) }}" onsubmit="return confirm('Cancel this Personal subscription immediately in Paddle? This cannot be undone.');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="danger">Cancel Personal</button>
|
||||
</form>
|
||||
</div>
|
||||
@elseif($license->edition === 'Personal')
|
||||
<div class="upgrade-strip">
|
||||
<div>
|
||||
<strong>Need more Macs?</strong>
|
||||
<span>Upgrade this subscription to Professional for up to 3 Macs.</span>
|
||||
</div>
|
||||
<form method="post" action="{{ route('portal.upgrade', $license) }}" onsubmit="return confirm('Upgrade this existing Paddle subscription to Professional now? Paddle will prorate the change.');">
|
||||
@csrf
|
||||
<button>Upgrade to Professional</button>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</article>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($historyLicences->isNotEmpty())
|
||||
<details class="history-panel">
|
||||
<summary>Licence history <span>{{ $historyLicences->count() }}</span></summary>
|
||||
<div class="history-list">
|
||||
@foreach($historyLicences as $license)
|
||||
<div class="history-row">
|
||||
<div>
|
||||
<strong>Rsync Studio {{ $license->edition }}</strong>
|
||||
<span>Licence ending {{ $license->license_key_last4 }}</span>
|
||||
</div>
|
||||
<div class="history-status">{{ ucfirst($license->subscription_status ?: ($license->revoked_at ? 'revoked' : 'unavailable')) }}</div>
|
||||
<a href="{{ route('portal.show', $license) }}">Details</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</details>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
Regular → Executable
+80
-1
@@ -1 +1,80 @@
|
||||
@extends('layouts.app') @section('content')<div class="card"><h1>{{ $license->product->name }}</h1><p>{{ $license->edition }} · key ending {{ $license->license_key_last4 }}</p><form method="post" action="{{ route('portal.resend',$license) }}">@csrf<button>Email my licence key</button></form></div><div class="card"><h2>Installations</h2><table><tr><th>Installation</th><th>Platform</th><th>Version</th><th>Last seen</th><th></th></tr>@foreach($license->activations->whereNull('deactivated_at') as $a)<tr><td>{{ $a->installation_name ?: $a->installation_id }}</td><td>{{ $a->platform }}</td><td>{{ $a->app_version }}</td><td>{{ optional($a->last_seen_at)->toDayDateTimeString() }}</td><td><form method="post" action="{{ route('portal.deactivate',[$license,$a->id]) }}">@csrf @method('DELETE')<button class="danger">Deactivate</button></form></td></tr>@endforeach</table></div>@endsection
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$activeActivations = $license->activations->whereNull('deactivated_at');
|
||||
$billing = str_contains(strtolower((string) optional($license->product)->code), 'annual') ? 'Annual' : 'Monthly';
|
||||
@endphp
|
||||
|
||||
<a class="back-link" href="{{ route('portal.index') }}">← My licences</a>
|
||||
|
||||
<section class="licence-detail-head">
|
||||
<div>
|
||||
<div class="badge-row">
|
||||
<span class="status-badge {{ $license->is_usable ? 'status-active' : 'status-inactive' }}">{{ $license->is_usable ? 'Active' : 'Unavailable' }}</span>
|
||||
<span class="plan-badge">{{ $license->edition }}</span>
|
||||
<span class="billing-badge">{{ $billing }}</span>
|
||||
</div>
|
||||
<h1>Rsync Studio {{ $license->edition }}</h1>
|
||||
<p class="muted">Licence ending <strong>{{ $license->license_key_last4 }}</strong></p>
|
||||
</div>
|
||||
<form method="post" action="{{ route('portal.resend', $license) }}">
|
||||
@csrf
|
||||
<button class="btn-secondary">Email licence key</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="detail-grid">
|
||||
<section class="panel">
|
||||
<div class="panel-heading">
|
||||
<div>
|
||||
<h2>Activated Macs</h2>
|
||||
<p>{{ $activeActivations->count() }} of {{ $license->activation_limit }} activation slots in use.</p>
|
||||
</div>
|
||||
<span class="activation-count">{{ $activeActivations->count() }}/{{ $license->activation_limit }}</span>
|
||||
</div>
|
||||
|
||||
@forelse($activeActivations as $activation)
|
||||
<div class="device-row">
|
||||
<div class="device-icon">⌘</div>
|
||||
<div class="device-copy">
|
||||
<strong>{{ $activation->installation_name ?: 'Mac' }}</strong>
|
||||
<span>{{ $activation->platform ?: 'macOS' }}@if($activation->app_version) · Rsync Studio {{ $activation->app_version }}@endif</span>
|
||||
<small>Last seen {{ optional($activation->last_seen_at)->diffForHumans() ?: 'not yet recorded' }}</small>
|
||||
</div>
|
||||
<form method="post" action="{{ route('portal.deactivate', [$license, $activation->id]) }}" onsubmit="return confirm('Deactivate this Mac? Rsync Studio on that Mac will lose access when it next validates.');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="danger-outline">Deactivate</button>
|
||||
</form>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-inline">No Macs are currently activated with this licence.</div>
|
||||
@endforelse
|
||||
</section>
|
||||
|
||||
<aside class="panel subscription-panel">
|
||||
<h2>Subscription</h2>
|
||||
<dl class="subscription-facts">
|
||||
<div><dt>Plan</dt><dd>{{ $license->edition }} {{ $billing }}</dd></div>
|
||||
<div><dt>Status</dt><dd>{{ ucfirst($license->subscription_status ?: 'active') }}</dd></div>
|
||||
<div><dt>{{ $license->subscription_status === 'trialing' ? 'Trial ends' : 'Current period ends' }}</dt><dd>{{ optional($license->current_period_ends_at)->format('j M Y') ?: '—' }}</dd></div>
|
||||
</dl>
|
||||
|
||||
@if($license->edition === 'Personal' && $license->is_usable)
|
||||
<form method="post" action="{{ route('portal.upgrade', $license) }}" onsubmit="return confirm('Upgrade this existing Paddle subscription to Professional now? Paddle will prorate the change.');">
|
||||
@csrf
|
||||
<button class="full-width">Upgrade to Professional</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@if($license->payment_provider === 'paddle' && $license->external_subscription_id && $license->subscription_status !== 'canceled')
|
||||
<form method="post" action="{{ route('portal.cancel', $license) }}" onsubmit="return confirm('Cancel this Paddle subscription immediately? This cannot be undone.');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="danger-outline full-width">Cancel subscription</button>
|
||||
</form>
|
||||
@endif
|
||||
</aside>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
Regular → Executable
+147
-1
@@ -1 +1,147 @@
|
||||
@extends('layouts.app') @section('content')<div class="card"><h1>Rsync Studio licensing</h1><p>Manage your Rsync Studio licence, installations and recovery options securely.</p>@guest<a class="btn" href="{{ route('login') }}">Sign in</a>@endguest</div>@endsection
|
||||
@extends('layouts.app', [
|
||||
'title' => 'Rsync Studio — Native rsync for macOS',
|
||||
'description' => 'Create, schedule, watch and review local and SSH rsync jobs with a native macOS interface. Start a 7-day free trial.'
|
||||
])
|
||||
|
||||
@section('fullbleed')
|
||||
<main class="launch-main">
|
||||
<section class="launch-hero">
|
||||
<div class="launch-shell hero-grid">
|
||||
<div>
|
||||
<span class="launch-kicker">Native macOS rsync management</span>
|
||||
<h1>Rsync without the command-line friction.</h1>
|
||||
<p class="hero-copy">Build reliable local and SSH sync jobs, test them safely with Dry Run, schedule recurring transfers, watch folders for changes and keep a complete run history — all from one native Mac app.</p>
|
||||
<div class="hero-actions">
|
||||
<a class="btn btn-primary" href="{{ route('buy') }}">Start 7-day free trial</a>
|
||||
@guest<a class="btn btn-secondary" href="{{ route('login') }}">Open licence portal</a>@else<a class="btn btn-secondary" href="{{ route('portal.index') }}">Open my licences</a>@endguest
|
||||
</div>
|
||||
<p class="hero-note">Personal for 1 Mac · Professional for up to 3 Macs · Monthly or annual</p>
|
||||
<div class="trust-row" aria-label="Product highlights">
|
||||
<span class="trust-item"><span class="trust-check">✓</span> Native macOS app</span>
|
||||
<span class="trust-item"><span class="trust-check">✓</span> Local & SSH jobs</span>
|
||||
<span class="trust-item"><span class="trust-check">✓</span> Dry Run safeguards</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="app-window" aria-label="Rsync Studio application preview">
|
||||
<div class="window-bar">
|
||||
<span class="dot red"></span><span class="dot amber"></span><span class="dot green"></span>
|
||||
<span class="window-title">Rsync Studio — Jobs</span>
|
||||
</div>
|
||||
<div class="app-body">
|
||||
<aside class="app-sidebar">
|
||||
<div class="mini-brand"><img src="{{ asset('icons/rsync-studio-icon-64.png') }}" alt=""><span>Rsync Studio</span></div>
|
||||
<div class="side-item active">Jobs</div>
|
||||
<div class="side-item">Calendar</div>
|
||||
<div class="side-item">History</div>
|
||||
<div class="side-item">Settings</div>
|
||||
</aside>
|
||||
<div class="app-content">
|
||||
<div class="app-toolbar"><strong>Sync jobs</strong><span class="sync-all">Sync All</span></div>
|
||||
<div class="mock-job">
|
||||
<div class="mock-job-top"><strong>Developer</strong><span class="job-state state-ready">Ready</span></div>
|
||||
<p>Developer folder → remote server · SSH</p>
|
||||
<div class="mock-progress"><span style="width:100%"></span></div>
|
||||
</div>
|
||||
<div class="mock-job">
|
||||
<div class="mock-job-top"><strong>Web Sites</strong><span class="job-state state-scheduled">22:00</span></div>
|
||||
<p>Sites folder → remote server · Daily</p>
|
||||
<div class="mock-progress"><span style="width:72%"></span></div>
|
||||
</div>
|
||||
<div class="mock-job">
|
||||
<div class="mock-job-top"><strong>Project Watch</strong><span class="job-state state-watching">Watching</span></div>
|
||||
<p>Project folder · File watch</p>
|
||||
<div class="mock-progress"><span style="width:44%"></span></div>
|
||||
</div>
|
||||
<div class="app-statusbar"><span>Last run: Success</span><span>rsync 3.4.1</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="launch-section">
|
||||
<div class="section-shell">
|
||||
<div class="section-head center">
|
||||
<span class="eyebrow">Everything in one place</span>
|
||||
<h2>Built for everyday rsync work.</h2>
|
||||
<p>Keep the power of rsync while replacing repetitive commands and scattered scripts with a clear, repeatable workflow.</p>
|
||||
</div>
|
||||
<div class="feature-grid">
|
||||
<article class="feature-card"><div class="feature-icon">↔</div><h3>Local & SSH sync</h3><p>Create local copies or securely sync to remote Macs and Linux servers over SSH.</p></article>
|
||||
<article class="feature-card"><div class="feature-icon">✓</div><h3>Dry Run first</h3><p>Preview changes before files are transferred or deleted, so risky jobs can be checked before they run.</p></article>
|
||||
<article class="feature-card"><div class="feature-icon">◷</div><h3>Flexible schedules</h3><p>Run jobs once, hourly, daily, weekly, monthly or on your own recurring interval.</p></article>
|
||||
<article class="feature-card"><div class="feature-icon">⌁</div><h3>Watch for changes</h3><p>Use macOS file events to trigger selected jobs when files or folders change.</p></article>
|
||||
<article class="feature-card"><div class="feature-icon">≡</div><h3>Complete run history</h3><p>Review commands, results, stdout, stderr and transfer statistics for previous runs.</p></article>
|
||||
<article class="feature-card"><div class="feature-icon">⌘</div><h3>Mac-first experience</h3><p>Manage jobs, defaults, background operation and connection settings from a native macOS interface.</p></article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="launch-section workflow-section">
|
||||
<div class="section-shell">
|
||||
<div class="section-head">
|
||||
<span class="eyebrow">A safer workflow</span>
|
||||
<h2>From setup to sync in four clear steps.</h2>
|
||||
</div>
|
||||
<div class="workflow-grid">
|
||||
<article class="workflow-step"><div class="step-number">1</div><h3>Create a job</h3><p>Choose your source, destination and rsync options once.</p></article>
|
||||
<article class="workflow-step"><div class="step-number">2</div><h3>Run a Dry Run</h3><p>Check the generated operation before changing any files.</p></article>
|
||||
<article class="workflow-step"><div class="step-number">3</div><h3>Run or automate</h3><p>Start it manually, schedule it or attach a file watcher.</p></article>
|
||||
<article class="workflow-step"><div class="step-number">4</div><h3>Review history</h3><p>See the result and retain the command and transfer output.</p></article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="launch-section">
|
||||
<div class="section-shell">
|
||||
<div class="security-panel">
|
||||
<div>
|
||||
<span class="eyebrow" style="color:#93c5fd">Designed around your Mac</span>
|
||||
<h2>Your sync workflow stays direct.</h2>
|
||||
<p>Rsync Studio runs rsync and SSH from your Mac. The licensing service is separate from your file-transfer path.</p>
|
||||
</div>
|
||||
<div class="security-list">
|
||||
<div class="security-item"><div class="security-icon">K</div><div><strong>Keychain-aware settings</strong><span>Stored passwords use macOS Keychain rather than application preferences.</span></div></div>
|
||||
<div class="security-item"><div class="security-icon">S</div><div><strong>SSH-native connections</strong><span>Use the standard macOS SSH client, keys and known-host verification for remote jobs.</span></div></div>
|
||||
<div class="security-item"><div class="security-icon">D</div><div><strong>Dry Run safeguards</strong><span>Test jobs before enabling destructive options such as destination deletion.</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="launch-section" id="pricing">
|
||||
<div class="section-shell">
|
||||
<div class="section-head center">
|
||||
<span class="eyebrow">Simple licensing</span>
|
||||
<h2>Choose the number of Macs you need.</h2>
|
||||
<p>Both plans include the full Rsync Studio workflow and a 7-day free trial. Checkout shows pricing in your supported local currency.</p>
|
||||
</div>
|
||||
<div class="plan-grid">
|
||||
<article class="launch-plan">
|
||||
<h3>Personal</h3>
|
||||
<p>For a single Mac and an individual sync workflow.</p>
|
||||
<div class="plan-macs">1 Mac</div><div class="plan-sub">Monthly or annual subscription</div>
|
||||
<ul class="plan-list"><li>Local and SSH jobs</li><li>Scheduling and file watch</li><li>Dry Run and history</li><li>7-day free trial</li></ul>
|
||||
<a class="btn btn-secondary" href="{{ route('buy') }}">Choose Personal</a>
|
||||
</article>
|
||||
<article class="launch-plan featured">
|
||||
<span class="best-badge">Most flexible</span>
|
||||
<h3>Professional</h3>
|
||||
<p>For multiple Macs or a broader development workflow.</p>
|
||||
<div class="plan-macs">Up to 3 Macs</div><div class="plan-sub">Monthly or annual subscription</div>
|
||||
<ul class="plan-list"><li>Everything in Personal</li><li>Three active Mac installations</li><li>Manage activations from the portal</li><li>7-day free trial</li></ul>
|
||||
<a class="btn btn-primary" href="{{ route('buy') }}">Choose Professional</a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="launch-cta">
|
||||
<div class="cta-box">
|
||||
<div><h2>Make rsync easier to run every day.</h2><p>Start with a 7-day free trial and choose Personal or Professional at checkout.</p></div>
|
||||
<a class="btn" href="{{ route('buy') }}">Start free trial</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@endsection
|
||||
|
||||
Reference in New Issue
Block a user