From 4cf5507bde20c1ed9cae440c07cf1ccb12c846df Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sun, 29 Mar 2026 18:16:08 +0100 Subject: [PATCH 1/2] Add Wall of Love edit profile, upgrade forms to Flux, remove old views Allow Wall of Love users to edit their photo and company name without re-approval. Add nav item for eligible users, upgrade both Showcase and Wall of Love forms to Flux UI components, and remove old unused controller-based views and the WallOfLoveBanner component. Co-Authored-By: Claude Opus 4.6 --- app/Livewire/Customer/WallOfLove/Edit.php | 20 + app/Livewire/WallOfLoveBanner.php | 35 -- app/Livewire/WallOfLoveSubmissionForm.php | 69 ++- package-lock.json | 2 +- .../views/components/dashboard-menu.blade.php | 62 --- .../components/layouts/dashboard.blade.php | 11 + resources/views/customer/dashboard.blade.php | 166 ------ .../customer/developer/dashboard.blade.php | 246 --------- .../views/customer/integrations.blade.php | 91 ---- .../views/customer/licenses/index.blade.php | 419 --------------- .../views/customer/licenses/list.blade.php | 206 -------- .../views/customer/licenses/show.blade.php | 488 ------------------ .../customer/purchase-history/index.blade.php | 145 ------ .../purchased-plugins/index.blade.php | 252 --------- .../views/customer/showcase/create.blade.php | 52 -- .../views/customer/showcase/edit.blade.php | 48 -- .../views/customer/showcase/index.blade.php | 133 ----- .../customer/wall-of-love/create.blade.php | 67 --- .../livewire/customer/dashboard.blade.php | 1 - .../customer/showcase/create.blade.php | 2 +- .../livewire/customer/showcase/edit.blade.php | 2 +- .../customer/wall-of-love/create.blade.php | 2 +- .../customer/wall-of-love/edit.blade.php | 10 + .../showcase-submission-form.blade.php | 229 +++----- .../livewire/wall-of-love-banner.blade.php | 30 -- .../wall-of-love-submission-form.blade.php | 94 ++-- routes/web.php | 1 + tests/Feature/WallOfLoveEditTest.php | 155 ++++++ 28 files changed, 384 insertions(+), 2654 deletions(-) create mode 100644 app/Livewire/Customer/WallOfLove/Edit.php delete mode 100644 app/Livewire/WallOfLoveBanner.php delete mode 100644 resources/views/components/dashboard-menu.blade.php delete mode 100644 resources/views/customer/dashboard.blade.php delete mode 100644 resources/views/customer/developer/dashboard.blade.php delete mode 100644 resources/views/customer/integrations.blade.php delete mode 100644 resources/views/customer/licenses/index.blade.php delete mode 100644 resources/views/customer/licenses/list.blade.php delete mode 100644 resources/views/customer/licenses/show.blade.php delete mode 100644 resources/views/customer/purchase-history/index.blade.php delete mode 100644 resources/views/customer/purchased-plugins/index.blade.php delete mode 100644 resources/views/customer/showcase/create.blade.php delete mode 100644 resources/views/customer/showcase/edit.blade.php delete mode 100644 resources/views/customer/showcase/index.blade.php delete mode 100644 resources/views/customer/wall-of-love/create.blade.php create mode 100644 resources/views/livewire/customer/wall-of-love/edit.blade.php delete mode 100644 resources/views/livewire/wall-of-love-banner.blade.php create mode 100644 tests/Feature/WallOfLoveEditTest.php diff --git a/app/Livewire/Customer/WallOfLove/Edit.php b/app/Livewire/Customer/WallOfLove/Edit.php new file mode 100644 index 000000000..8b7dba9bd --- /dev/null +++ b/app/Livewire/Customer/WallOfLove/Edit.php @@ -0,0 +1,20 @@ +user_id !== auth()->id(), 403); + } +} diff --git a/app/Livewire/WallOfLoveBanner.php b/app/Livewire/WallOfLoveBanner.php deleted file mode 100644 index 292ecd7d3..000000000 --- a/app/Livewire/WallOfLoveBanner.php +++ /dev/null @@ -1,35 +0,0 @@ -put('wall_of_love_dismissed_'.auth()->id(), true, now()->addWeek()); - $this->dispatch('banner-dismissed'); - } - - public function shouldShowBanner(): bool - { - // Check if user has early adopter licenses (before June 1st, 2025) - $hasEarlyAdopterLicenses = auth()->user()->licenses()->where('created_at', '<', '2025-06-01')->exists(); - - // Check if user already submitted - $hasExistingSubmission = auth()->user()->wallOfLoveSubmissions()->exists(); - - // Check if banner was dismissed - $hasDismissedBanner = cache()->has('wall_of_love_dismissed_'.auth()->id()); - - return $hasEarlyAdopterLicenses && ! $hasExistingSubmission && ! $hasDismissedBanner; - } - - public function render() - { - return view('livewire.wall-of-love-banner'); - } -} diff --git a/app/Livewire/WallOfLoveSubmissionForm.php b/app/Livewire/WallOfLoveSubmissionForm.php index b74074652..de427e396 100644 --- a/app/Livewire/WallOfLoveSubmissionForm.php +++ b/app/Livewire/WallOfLoveSubmissionForm.php @@ -10,34 +10,76 @@ class WallOfLoveSubmissionForm extends Component { use WithFileUploads; + public ?WallOfLoveSubmission $submission = null; + + public bool $isEditing = false; + public $name = ''; public $company = ''; public $photo; + public ?string $existingPhoto = null; + public $url = ''; public $testimonial = ''; - protected $rules = [ - 'name' => 'required|string|max:255', - 'company' => 'nullable|string|max:255', - 'photo' => 'nullable|image|max:2048', // 2MB max - 'url' => 'nullable|url|max:255', - 'testimonial' => 'nullable|string|max:1000', - ]; + public function mount(?WallOfLoveSubmission $submission = null): void + { + if ($submission && $submission->exists && $submission->user_id === auth()->id()) { + $this->submission = $submission; + $this->isEditing = true; + $this->name = $submission->name; + $this->company = $submission->company ?? ''; + $this->existingPhoto = $submission->photo_path; + $this->url = $submission->url ?? ''; + $this->testimonial = $submission->testimonial ?? ''; + } else { + // Pre-fill name if user has a name + $this->name = auth()->user()->name ?? ''; + } + } - public function mount() + public function rules(): array { - // Pre-fill name if user has a name - $this->name = auth()->user()->name ?? ''; + if ($this->isEditing) { + return [ + 'company' => 'nullable|string|max:255', + 'photo' => 'nullable|image|max:2048', + ]; + } + + return [ + 'name' => 'required|string|max:255', + 'company' => 'nullable|string|max:255', + 'photo' => 'nullable|image|max:2048', + 'url' => 'nullable|url|max:255', + 'testimonial' => 'nullable|string|max:1000', + ]; } - public function submit() + public function submit(): mixed { $this->validate(); + if ($this->isEditing && $this->submission) { + $data = [ + 'company' => $this->company ?: null, + ]; + + if ($this->photo) { + $data['photo_path'] = $this->photo->store('wall-of-love-photos', 'public'); + } elseif ($this->existingPhoto === null) { + $data['photo_path'] = null; + } + + $this->submission->update($data); + + return to_route('dashboard')->with('success', 'Your listing has been updated.'); + } + $photoPath = null; if ($this->photo) { $photoPath = $this->photo->store('wall-of-love-photos', 'public'); @@ -55,6 +97,11 @@ public function submit() return to_route('dashboard')->with('success', 'Thank you! Your submission has been received and is awaiting review.'); } + public function removeExistingPhoto(): void + { + $this->existingPhoto = null; + } + public function render() { return view('livewire.wall-of-love-submission-form'); diff --git a/package-lock.json b/package-lock.json index 214610184..1ca1fd9b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "kind-goose", + "name": "cyan-grizzly", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/resources/views/components/dashboard-menu.blade.php b/resources/views/components/dashboard-menu.blade.php deleted file mode 100644 index cedcb3c45..000000000 --- a/resources/views/components/dashboard-menu.blade.php +++ /dev/null @@ -1,62 +0,0 @@ -
- - -
- - Dashboard - - - Licenses - - @feature(App\Features\ShowPlugins::class) - - Purchased Plugins - - @endfeature - - Purchase History - -
- - Showcase - - @feature(App\Features\ShowPlugins::class) - - Developer Plugins - - @endfeature - - Integrations - - - Manage Subscription - -
-
- @csrf - -
-
-
diff --git a/resources/views/components/layouts/dashboard.blade.php b/resources/views/components/layouts/dashboard.blade.php index 0fda420cf..fc5740cfa 100644 --- a/resources/views/components/layouts/dashboard.blade.php +++ b/resources/views/components/layouts/dashboard.blade.php @@ -115,6 +115,17 @@ class="min-h-screen bg-white font-poppins antialiased dark:bg-zinc-900 dark:text Showcase + @if(auth()->user()->licenses()->where('created_at', '<', '2025-06-01')->exists()) + @php + $wallOfLoveSubmission = auth()->user()->wallOfLoveSubmissions()->first(); + $wallOfLoveUrl = $wallOfLoveSubmission + ? route('customer.wall-of-love.edit', $wallOfLoveSubmission) + : route('customer.wall-of-love.create'); + @endphp + + Wall of Love + + @endif Discord diff --git a/resources/views/customer/dashboard.blade.php b/resources/views/customer/dashboard.blade.php deleted file mode 100644 index 78608b032..000000000 --- a/resources/views/customer/dashboard.blade.php +++ /dev/null @@ -1,166 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
-

Dashboard

-

- Welcome back, {{ auth()->user()->first_name ?? auth()->user()->name }} -

-
- -
-
-
- - {{-- Banners --}} -
-
- @feature(App\Features\ShowPlugins::class) - @if(auth()->user()->shouldSeeFreePluginsOffer()) - - @endif - @else - - @endfeature - -
-
- - {{-- Updated Terms Banner --}} - @if ($developerAccount && $developerAccount->hasAcceptedPluginTerms() && !$developerAccount->hasAcceptedCurrentTerms()) -
-
-
-
- - - -

- Our Plugin Developer Terms and Conditions have been updated. You must accept the new terms before you can submit new plugins. -

-
- - Review & Accept - -
-
-
- @endif - - {{-- Dashboard Cards --}} -
-
- {{-- Licenses Card --}} - - - {{-- EAP Status Card --}} - - - {{-- Subscription Card --}} - @if($activeSubscription) - - @else - - @endif - - {{-- Premium Plugins Card --}} - @feature(App\Features\ShowPlugins::class) - - - {{-- Submit Plugin Card --}} - - @endfeature - - {{-- Connected Accounts Card --}} - - - {{-- Purchase History Card --}} - -
-
- - {{-- Session Messages --}} -
- @if (session('success')) -
-

{{ session('success') }}

-
- @endif - - @if (session('message')) -
-

{{ session('message') }}

-
- @endif - - @if (session('error')) -
-

{{ session('error') }}

-
- @endif -
-
-
diff --git a/resources/views/customer/developer/dashboard.blade.php b/resources/views/customer/developer/dashboard.blade.php deleted file mode 100644 index 33ae03c3b..000000000 --- a/resources/views/customer/developer/dashboard.blade.php +++ /dev/null @@ -1,246 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
-

Developer Dashboard

-

- Manage your plugins and track your earnings -

-
- -
-
-
- - {{-- Session Messages --}} -
- @if (session('success')) -
-

{{ session('success') }}

-
- @endif - - @if (session('message')) -
-

{{ session('message') }}

-
- @endif -
- - {{-- Updated Terms Banner --}} - @if ($developerAccount && $developerAccount->hasAcceptedPluginTerms() && !$developerAccount->hasAcceptedCurrentTerms()) -
-
-
-
- - - -

- Our Plugin Developer Terms and Conditions have been updated. You must accept the new terms before you can submit new plugins. -

-
- - Review & Accept - -
-
-
- @endif - - {{-- Content --}} -
- {{-- Stats Grid --}} -
- {{-- Total Earnings --}} -
-
Total Earnings
-
- ${{ number_format($totalEarnings / 100, 2) }} -
-
- - {{-- Pending Earnings --}} -
-
Pending Payouts
-
- ${{ number_format($pendingEarnings / 100, 2) }} -
-
- - {{-- Total Plugins --}} -
-
Published Plugins
-
- {{ $plugins->where('status', \App\Enums\PluginStatus::Approved)->count() }} -
-
- - {{-- Total Sales --}} -
-
Total Sales
-
- {{ $plugins->sum('licenses_count') }} -
-
-
- - {{-- Account Status --}} -
-
-

Stripe Account Status

-
-
-
-
- @if ($developerAccount->canReceivePayouts()) -
- - - -
-
-

Account Active

-

Your account is fully set up to receive payouts

-
- @else -
- - - -
-
-

Action Required

-

Additional information needed for payouts

-
- @endif -
- @if (! $developerAccount->canReceivePayouts()) - - Complete Setup - - @endif -
-
-
- - {{-- Two Column Layout --}} -
- {{-- Plugins --}} - - - {{-- Recent Payouts --}} -
-
-

Recent Payouts

-
-
- @forelse ($payouts as $payout) -
-
-
-

- {{ $payout->pluginLicense->plugin->name ?? 'Unknown Plugin' }} -

-

- {{ $payout->created_at->format('M j, Y') }} -

-
-
-

- ${{ number_format($payout->developer_amount / 100, 2) }} -

- @if ($payout->status === \App\Enums\PayoutStatus::Transferred) - - Paid - - @elseif ($payout->status === \App\Enums\PayoutStatus::Pending) - - Pending - - @else - - Failed - - @endif -
-
-
- @empty -
- - - -

No payouts yet

-

Payouts will appear here after you make your first sale

-
- @endforelse -
-
-
-
-
-
diff --git a/resources/views/customer/integrations.blade.php b/resources/views/customer/integrations.blade.php deleted file mode 100644 index bd95a8419..000000000 --- a/resources/views/customer/integrations.blade.php +++ /dev/null @@ -1,91 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

Integrations

-

- Connect your accounts to unlock additional features -

-
- -
-
-
- - {{-- Content --}} -
- {{-- Flash Messages --}} - @if(session()->has('success')) -
-
- - - -

{{ session('success') }}

-
-
- @endif - - @if(session()->has('warning')) -
-
- - - -

{{ session('warning') }}

-
-
- @endif - - @if(session()->has('error')) -
-
- - - -

{{ session('error') }}

-
-
- @endif - - {{-- Info Section --}} -
-

About Integrations

-
-
    -
  • GitHub: Max license holders can access the private nativephp/mobile repository. Plugin Dev Kit license holders can access nativephp/claude-code.
  • -
  • Discord: Max license holders receive a special "Max" role in the NativePHP Discord server.
  • -
-

- Need help? Join our Discord community. -

-
-
- - {{-- Claude Plugins Access (for Plugin Dev Kit license holders) --}} -
- -
- - @if(auth()->user()->hasMaxAccess()) - {{-- Integration Cards --}} -
- {{-- GitHub Integration --}} - - - {{-- Discord Integration --}} - -
- @endif -
-
-
diff --git a/resources/views/customer/licenses/index.blade.php b/resources/views/customer/licenses/index.blade.php deleted file mode 100644 index 5e699824c..000000000 --- a/resources/views/customer/licenses/index.blade.php +++ /dev/null @@ -1,419 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
-

Your Licenses

-

- Manage your NativePHP licenses -

-
- -
-
-
- - {{-- Banners --}} -
-
- @feature(App\Features\ShowPlugins::class) - @if(auth()->user()->shouldSeeFreePluginsOffer()) - - @endif - @else - - @endfeature - -
-
- - {{-- Plugin Composer Configuration --}} - @feature(App\Features\ShowPlugins::class) -
-
-

Plugins

- - Browse plugins - -
- - {{-- Plugin License Key - Always shown --}} -
-
-
-

Your Plugin Credentials

-

- Use these credentials with Composer to install plugins from the NativePHP Plugin Marketplace. -

-
- -
-
- - How to configure Composer - -
-

1. Add the NativePHP plugins repository:

-
-
- composer config repositories.nativephp-plugins composer https://plugins.nativephp.com -
- -
-

2. Configure your credentials:

-
-
- composer config http-basic.plugins.nativephp.com {{ auth()->user()->email }} {{ $pluginLicenseKey }} -
- -
-
-
- - {{-- Rotate Key Confirmation Modal --}} - -
- - {{-- Purchased Plugins --}} - @if($pluginLicenses->count() > 0) -

Your Purchased Plugins

- - @endif -
- @endfeature - - {{-- Content --}} -
-

Licenses

- @if($licenses->count() > 0) - - @endif - - {{-- Assigned Sub-Licenses --}} - @if($assignedSubLicenses->count() > 0) -
-

Assigned Sub-Licenses

-
-
    - @foreach($assignedSubLicenses as $subLicense) -
  • -
    -
    -
    -
    - @if($subLicense->is_suspended) -
    - @elseif($subLicense->expires_at && $subLicense->expires_at->isPast()) -
    - @else -
    - @endif -
    -
    -
    -
    -

    - {{ $subLicense->parentLicense->policy_name ?? 'Sub-License' }} -

    -

    - Sub-license -

    -
    - @if($subLicense->is_suspended) - - Suspended - - @elseif($subLicense->expires_at && $subLicense->expires_at->isPast()) - - Expired - - @else - - Active - - @endif -
    -

    - {{ $subLicense->key }} -

    -
    -
    -
    -

    - @if($subLicense->expires_at) - Expires {{ $subLicense->expires_at->format('M j, Y') }} - @else - No expiration - @endif -

    -

    - Assigned {{ $subLicense->created_at->format('M j, Y') }} -

    -
    -
    -
    -
  • - @endforeach -
-
-
- @endif - - @if($licenses->count() === 0 && $assignedSubLicenses->count() === 0) -
-
-

No licenses found

-

- You don't have any licenses yet. If you believe this is an error, please contact support. -

-
-
- @endif -
-
-
diff --git a/resources/views/customer/licenses/list.blade.php b/resources/views/customer/licenses/list.blade.php deleted file mode 100644 index 640058ccc..000000000 --- a/resources/views/customer/licenses/list.blade.php +++ /dev/null @@ -1,206 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

Your Licenses

-

- Manage your NativePHP licenses -

-
- -
-
-
- - {{-- Content --}} -
- @if($licenses->count() > 0) - - @endif - - {{-- Assigned Sub-Licenses --}} - @if($assignedSubLicenses->count() > 0) -
-

Assigned Sub-Licenses

-
-
    - @foreach($assignedSubLicenses as $subLicense) -
  • -
    -
    -
    -
    - @if($subLicense->is_suspended) -
    - @elseif($subLicense->expires_at && $subLicense->expires_at->isPast()) -
    - @else -
    - @endif -
    -
    -
    -
    -

    - {{ $subLicense->parentLicense->policy_name ?? 'Sub-License' }} -

    -

    - Sub-license -

    -
    - @if($subLicense->is_suspended) - - Suspended - - @elseif($subLicense->expires_at && $subLicense->expires_at->isPast()) - - Expired - - @else - - Active - - @endif -
    -

    - {{ $subLicense->key }} -

    -
    -
    -
    -

    - @if($subLicense->expires_at) - Expires {{ $subLicense->expires_at->format('M j, Y') }} - @else - No expiration - @endif -

    -

    - Assigned {{ $subLicense->created_at->format('M j, Y') }} -

    -
    -
    -
    -
  • - @endforeach -
-
-
- @endif - - @if($licenses->count() === 0 && $assignedSubLicenses->count() === 0) -
-
- -

No licenses found

-

- You don't have any licenses yet. If you believe this is an error, please contact support. -

-
-
- @endif -
-
-
diff --git a/resources/views/customer/licenses/show.blade.php b/resources/views/customer/licenses/show.blade.php deleted file mode 100644 index 8d2aa857d..000000000 --- a/resources/views/customer/licenses/show.blade.php +++ /dev/null @@ -1,488 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

- {{ $license->name ?: $license->policy_name }} -

- @if($license->name) -

{{ $license->policy_name }}

- @endif -
- -
-
-
- - {{-- Content --}} -
-
-
-
-
-

- License Information -

-

- Details about your NativePHP license. -

-
-
- @if($license->is_suspended) - - - - - Suspended - - @elseif($license->expires_at && $license->expires_at->isPast()) - - - - - Expired - - @else - - - - - Active - - @endif -
-
-
-
-
-
-
- License Key -
-
-
- - {{ $license->key }} - - -
-
-
-
-
- License Name -
-
-
- - {{ $license->name ?: 'No name set' }} - - -
-
-
-
-
- License Type -
-
- {{ $license->policy_name }} -
-
-
-
- Created -
-
- {{ $license->created_at->format('F j, Y \a\t g:i A') }} - - ({{ $license->created_at->diffForHumans() }}) - -
-
- @if($license->expires_at) -
-
- Expires -
-
- {{ $license->expires_at->format('F j, Y \a\t g:i A') }} - - @if($license->expires_at->isPast()) - (Expired {{ $license->expires_at->diffForHumans() }}) - @else - ({{ $license->expires_at->diffForHumans() }}) - @endif - -
-
- @else -
-
- Expires -
-
- Never -
-
- @endif -
-
-
- - {{-- Keys section --}} - @if($license->supportsSubLicenses()) - @livewire('sub-license-manager', ['license' => $license]) - @endif - - @php - $isLegacyLicense = $license->isLegacy(); - $daysUntilExpiry = $license->expires_at ? (int) now()->diffInDays($license->expires_at, false) : null; - $needsRenewal = $isLegacyLicense && $daysUntilExpiry !== null; - @endphp - - @if($needsRenewal && !$license->expires_at->isPast()) -
-
-
- - - -
-
-

- 🎉 Renewal Available with Early Access Pricing -

-
-

- Your license expires in {{ $daysUntilExpiry }} day{{ $daysUntilExpiry === 1 ? '' : 's' }}. - Set up automatic renewal now to avoid interruption and lock in your Early Access Pricing! -

- -
-
-
-
- @endif - - @if($license->is_suspended || ($license->expires_at && $license->expires_at->isPast())) -
-
-
- -
-
-

- @if($license->is_suspended) - License Suspended - @else - License Expired - @endif -

-
-

- @if($license->is_suspended) - This license has been suspended. Please contact support for assistance. - @elseif($isLegacyLicense) - This license has expired. You can still renew it to restore access. - - Renew now - - @else - This license has expired. Please renew your subscription to continue using NativePHP. - @endif -

-
-
-
-
- @endif -
-
- - {{-- Create Key Modal --}} - - - {{-- Edit Key Modal --}} - - - {{-- Edit License Name Modal --}} - - - -
diff --git a/resources/views/customer/purchase-history/index.blade.php b/resources/views/customer/purchase-history/index.blade.php deleted file mode 100644 index 46e1b80fe..000000000 --- a/resources/views/customer/purchase-history/index.blade.php +++ /dev/null @@ -1,145 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

Purchase History

-

- All your NativePHP purchases in one place -

-
- -
-
-
- - {{-- Content --}} - -
-
diff --git a/resources/views/customer/purchased-plugins/index.blade.php b/resources/views/customer/purchased-plugins/index.blade.php deleted file mode 100644 index 53b96f0cb..000000000 --- a/resources/views/customer/purchased-plugins/index.blade.php +++ /dev/null @@ -1,252 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

Purchased Plugins

-

- Your premium plugins and Composer configuration -

-
- -
-
-
- - {{-- Content --}} -
- {{-- Plugin License Key / Composer Configuration --}} -
-
-
-

Your Plugin Credentials

-

- Use these credentials with Composer to install plugins from the NativePHP Plugin Marketplace. -

-
- -
-
- - How to configure Composer - -
-

1. Add the NativePHP plugins repository:

-
-
- composer config repositories.nativephp-plugins composer https://plugins.nativephp.com -
- -
-

2. Configure your credentials:

-
-
- composer config http-basic.plugins.nativephp.com {{ auth()->user()->email }} {{ $pluginLicenseKey }} -
- -
-
-
- - {{-- Rotate Key Confirmation Modal --}} - -
- - {{-- Session Messages --}} - @if (session('success')) -
-

{{ session('success') }}

-
- @endif - - {{-- Purchased Plugins List --}} - @if($pluginLicenses->count() > 0) -

Your Plugins

- - @else -
-
- -

No plugins yet

-

- Browse the plugin marketplace to find premium plugins for your NativePHP app. -

- -
-
- @endif -
-
-
diff --git a/resources/views/customer/showcase/create.blade.php b/resources/views/customer/showcase/create.blade.php deleted file mode 100644 index b39766df7..000000000 --- a/resources/views/customer/showcase/create.blade.php +++ /dev/null @@ -1,52 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - - -

Submit Your App to the Showcase

-

- Share your NativePHP app with the community! Your submission will be reviewed by our team. -

-
- -
-
-
- - {{-- Content --}} -
-
-
- {{-- Info Box --}} -
-
-
-

- Showcase Guidelines -

-
    -
  • Your app must be built with NativePHP
  • -
  • Include clear screenshots showcasing your app
  • -
  • Provide download links or store URLs where users can get your app
  • -
  • Submissions are reviewed before being published
  • -
-
-
-
- - {{-- Submission Form --}} - -
-
-
-
-
diff --git a/resources/views/customer/showcase/edit.blade.php b/resources/views/customer/showcase/edit.blade.php deleted file mode 100644 index d1717f7fb..000000000 --- a/resources/views/customer/showcase/edit.blade.php +++ /dev/null @@ -1,48 +0,0 @@ - -
- {{-- Header --}} -
-
-
- - - - - Dashboard - - -
-
-

Edit Your Submission

-

- Update the details of your showcase submission. -

-
-
- @if($showcase->isApproved()) - - Approved - - @else - - Pending Review - - @endif - -
-
-
-
-
- - {{-- Content --}} -
-
-
- {{-- Submission Form --}} - -
-
-
-
-
diff --git a/resources/views/customer/showcase/index.blade.php b/resources/views/customer/showcase/index.blade.php deleted file mode 100644 index 6c17daec0..000000000 --- a/resources/views/customer/showcase/index.blade.php +++ /dev/null @@ -1,133 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - -

Your Showcase Submissions

-

- Submit your NativePHP apps to be featured on our showcase -

-
- -
-
-
- - {{-- Messages --}} -
- @if(session()->has('success')) -
-
- - - -

{{ session('success') }}

-
-
- @endif - - @if(session()->has('warning')) -
-
- - - -

{{ session('warning') }}

-
-
- @endif -
- - {{-- Content --}} -
- @if($showcases->count() > 0) - - @else -
-
- - - -

No submissions yet

-

- Get started by submitting your first NativePHP app to the showcase. -

- -
-
- @endif -
-
-
diff --git a/resources/views/customer/wall-of-love/create.blade.php b/resources/views/customer/wall-of-love/create.blade.php deleted file mode 100644 index f535e32e2..000000000 --- a/resources/views/customer/wall-of-love/create.blade.php +++ /dev/null @@ -1,67 +0,0 @@ - -
- {{-- Header --}} -
-
-
-
- - - - - Dashboard - - -

Join our Wall of Love! 💙

-

- As an early adopter, your story matters. Share your experience with NativePHP and inspire other developers in the community. -

-
- -
-
-
- - {{-- Content --}} -
-
-
- {{-- Success Message --}} - @if(session()->has('success')) -
-
- - - -

{{ session('success') }}

-
-
- @endif - - {{-- Info about early adopter status --}} -
-
-
-

- You're an Early Adopter! -

-

- Thank you for supporting NativePHP from the beginning. As a reward, you can appear - permanently on our - Wall of Love. -

-

- Your submission will be reviewed by our team and, once approved, will appear - on the page. -

-
-
-
- - {{-- Submission Form --}} - -
-
-
-
-
diff --git a/resources/views/livewire/customer/dashboard.blade.php b/resources/views/livewire/customer/dashboard.blade.php index 9272a682e..496f51d57 100644 --- a/resources/views/livewire/customer/dashboard.blade.php +++ b/resources/views/livewire/customer/dashboard.blade.php @@ -32,7 +32,6 @@ @else @endfeature - {{-- Dashboard Cards --}} diff --git a/resources/views/livewire/customer/showcase/create.blade.php b/resources/views/livewire/customer/showcase/create.blade.php index 1dca77f7e..09f5b4213 100644 --- a/resources/views/livewire/customer/showcase/create.blade.php +++ b/resources/views/livewire/customer/showcase/create.blade.php @@ -1,4 +1,4 @@ -
+
Submit Your App to the Showcase Share your NativePHP app with the community! Your submission will be reviewed by our team. diff --git a/resources/views/livewire/customer/showcase/edit.blade.php b/resources/views/livewire/customer/showcase/edit.blade.php index 231ac23f3..ab5c017ed 100644 --- a/resources/views/livewire/customer/showcase/edit.blade.php +++ b/resources/views/livewire/customer/showcase/edit.blade.php @@ -1,4 +1,4 @@ -
+
Edit Your Submission diff --git a/resources/views/livewire/customer/wall-of-love/create.blade.php b/resources/views/livewire/customer/wall-of-love/create.blade.php index 85d3bbf5d..f0188a37b 100644 --- a/resources/views/livewire/customer/wall-of-love/create.blade.php +++ b/resources/views/livewire/customer/wall-of-love/create.blade.php @@ -1,4 +1,4 @@ -
+
Join our Wall of Love! As an early adopter, your story matters. Share your experience with NativePHP and inspire other developers in the community. diff --git a/resources/views/livewire/customer/wall-of-love/edit.blade.php b/resources/views/livewire/customer/wall-of-love/edit.blade.php new file mode 100644 index 000000000..a518ab0df --- /dev/null +++ b/resources/views/livewire/customer/wall-of-love/edit.blade.php @@ -0,0 +1,10 @@ +
+
+ Edit Your Listing + Update your photo and company name on the Wall of Love. +
+ + + + +
diff --git a/resources/views/livewire/showcase-submission-form.blade.php b/resources/views/livewire/showcase-submission-form.blade.php index 771ec43fa..f91f9857d 100644 --- a/resources/views/livewire/showcase-submission-form.blade.php +++ b/resources/views/livewire/showcase-submission-form.blade.php @@ -1,73 +1,52 @@
{{-- Warning for approved submissions being edited --}} @if ($isEditing && $showcase?->isApproved()) -
-
-
- - - -
-
-

Re-review Required

-

- This submission is currently approved. If you make changes, it will need to be reviewed again before appearing in the showcase. -

-
-
-
+ + Re-review Required + + This submission is currently approved. If you make changes, it will need to be reviewed again before appearing in the showcase. + + @endif {{-- Title Field --}} -
- - - @error('title')

{{ $message }}

@enderror -
+ + App Name * + + + {{-- Description Field --}} -
- - - @error('description')

{{ $message }}

@enderror -

Maximum 2000 characters.

-
+ + Description * + + + Maximum 2000 characters. + {{-- Main Image Field --}} -
- + + App Icon / Main Image @if ($existingImage) -
- Current image - +
+ Current image + Remove
@endif -
- -
- @error('image')

{{ $message }}

@enderror -

Max 2MB. Recommended: Square image, at least 256x256px.

-
+ + + Max 2MB. Recommended: Square image, at least 256x256px. +
{{-- Screenshots Field --}} -
- + + Screenshots (up to 5) @if (count($existingScreenshots) > 0) -
+
@foreach ($existingScreenshots as $index => $screenshot) -
- Screenshot {{ $index + 1 }} -
@endif @if (count($existingScreenshots) < 5) -
- -
-

- Max 2MB each. You can add {{ 5 - count($existingScreenshots) }} more screenshot(s). -

+ + Max 2MB each. You can add {{ 5 - count($existingScreenshots) }} more screenshot(s). @endif - @error('screenshots.*')

{{ $message }}

@enderror -
+ @error('screenshots.*') {{ $message }} @enderror + {{-- Platform Selection --}} -
- + + Available Platforms *
{{-- Mobile Toggle --}} -
-
- -
-
- -

iOS and/or Android

-
-
+ {{-- Mobile Links (shown when hasMobile is true) --}} @if ($hasMobile) -
-
- - - @error('appStoreUrl')

{{ $message }}

@enderror -
+
+ + App Store URL + + + -
- - - @error('playStoreUrl')

{{ $message }}

@enderror -
+ + Play Store URL + + +
@endif {{-- Desktop Toggle --}} -
-
- -
-
- -

Windows, macOS, and/or Linux

-
-
+ {{-- Desktop Links (shown when hasDesktop is true) --}} @if ($hasDesktop) -
-
- - - @error('windowsDownloadUrl')

{{ $message }}

@enderror -
+
+ + Windows Download URL + + + -
- - - @error('macosDownloadUrl')

{{ $message }}

@enderror -
+ + macOS Download URL + + + -
- - - @error('linuxDownloadUrl')

{{ $message }}

@enderror -
+ + Linux Download URL + + +
@endif
- @error('hasMobile')

{{ $message }}

@enderror -
+ + {{-- Certification Checkbox --}} -
-
-
- -
-
- -

- By checking this box, you confirm that your application is built using NativePHP. - Submissions found not to be built with NativePHP may be rejected or removed from the showcase. -

-
-
- @error('certifiedNativephp')

{{ $message }}

@enderror +
+ +
{{-- Form Actions --}} -
+
@if ($isEditing) - + @endif
-
- - Cancel - - +
+ Cancel + + {{ $isEditing ? 'Update Submission' : 'Submit App' }} +
diff --git a/resources/views/livewire/wall-of-love-banner.blade.php b/resources/views/livewire/wall-of-love-banner.blade.php deleted file mode 100644 index 77f6e7ded..000000000 --- a/resources/views/livewire/wall-of-love-banner.blade.php +++ /dev/null @@ -1,30 +0,0 @@ -
!$inline])> - @if($this->shouldShowBanner()) -
-
-
- - - -
-
-

- Join our Wall of Love! -

-

- As an early adopter who purchased a license before June 1st, 2025, we'd love to feature you on - our Wall of Love page. -

-
- - Submit Your Details - - -
-
-
-
- @endif -
diff --git a/resources/views/livewire/wall-of-love-submission-form.blade.php b/resources/views/livewire/wall-of-love-submission-form.blade.php index 4ac4d37e0..24023d342 100644 --- a/resources/views/livewire/wall-of-love-submission-form.blade.php +++ b/resources/views/livewire/wall-of-love-submission-form.blade.php @@ -1,58 +1,54 @@
- {{-- Name Field --}} -
- - - @error('name')

{{ $message }}

@enderror -
+ @if(! $isEditing) + {{-- Name Field --}} + + Name * + + + + @endif {{-- Company Field --}} -
- - - @error('company')

{{ $message }}

@enderror -
+ + Company (optional) + + + {{-- Photo Field --}} -
- -
- -
- @error('photo')

{{ $message }}

@enderror -
+ + Photo (optional) - {{-- URL Field --}} -
- - - @error('url')

{{ $message }}

@enderror -
+ @if($isEditing && $existingPhoto) +
+ Current photo + Remove photo +
+ @endif - {{-- Testimonial Field --}} -
- - - @error('testimonial')

{{ $message }}

@enderror -

Share what you built, how NativePHP helped you, or what you love about the framework.

-
+ + +
+ + @if(! $isEditing) + {{-- URL Field --}} + + Website or Social Media URL (optional) + + + + + {{-- Testimonial Field --}} + + Your story or testimonial (optional) + + + Share what you built, how NativePHP helped you, or what you love about the framework. + + @endif -
- - Cancel - - +
+ Cancel + {{ $isEditing ? 'Save Changes' : 'Submit Your Story' }}
- \ No newline at end of file + diff --git a/routes/web.php b/routes/web.php index 0cbb98e8f..b536c5078 100644 --- a/routes/web.php +++ b/routes/web.php @@ -348,6 +348,7 @@ // Wall of Love submission Route::livewire('wall-of-love/create', Create::class)->name('wall-of-love.create'); + Route::livewire('wall-of-love/{wallOfLoveSubmission}/edit', App\Livewire\Customer\WallOfLove\Edit::class)->name('wall-of-love.edit'); // Showcase submissions Route::livewire('showcase', App\Livewire\Customer\Showcase\Index::class)->name('showcase.index'); diff --git a/tests/Feature/WallOfLoveEditTest.php b/tests/Feature/WallOfLoveEditTest.php new file mode 100644 index 000000000..0a4ad7fbf --- /dev/null +++ b/tests/Feature/WallOfLoveEditTest.php @@ -0,0 +1,155 @@ +create(); + $submission = WallOfLoveSubmission::factory()->create(['user_id' => $user->id]); + + $response = $this->actingAs($user)->get("/dashboard/wall-of-love/{$submission->id}/edit"); + + $response->assertStatus(200); + $response->assertSee('Edit Your Listing'); + } + + public function test_non_owner_gets_403(): void + { + $owner = User::factory()->create(); + $otherUser = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->create(['user_id' => $owner->id]); + + $response = $this->actingAs($otherUser)->get("/dashboard/wall-of-love/{$submission->id}/edit"); + + $response->assertStatus(403); + } + + public function test_guest_cannot_access_edit_page(): void + { + $submission = WallOfLoveSubmission::factory()->create(); + + $response = $this->get("/dashboard/wall-of-love/{$submission->id}/edit"); + + $response->assertRedirect('/login'); + } + + public function test_owner_can_update_company_name(): void + { + $user = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->create([ + 'user_id' => $user->id, + 'company' => 'Old Company', + ]); + + Livewire::actingAs($user) + ->test(WallOfLoveSubmissionForm::class, ['submission' => $submission]) + ->assertSet('isEditing', true) + ->assertSet('company', 'Old Company') + ->set('company', 'New Company') + ->call('submit'); + + $this->assertDatabaseHas('wall_of_love_submissions', [ + 'id' => $submission->id, + 'company' => 'New Company', + ]); + } + + public function test_owner_can_update_photo(): void + { + Storage::fake('public'); + + $user = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->create([ + 'user_id' => $user->id, + 'photo_path' => null, + ]); + + Livewire::actingAs($user) + ->test(WallOfLoveSubmissionForm::class, ['submission' => $submission]) + ->set('photo', UploadedFile::fake()->image('avatar.jpg')) + ->call('submit'); + + $submission->refresh(); + $this->assertNotNull($submission->photo_path); + Storage::disk('public')->assertExists($submission->photo_path); + } + + public function test_approval_status_is_not_reset_on_edit(): void + { + $user = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->approved()->create([ + 'user_id' => $user->id, + 'company' => 'Old Company', + ]); + + $originalApprovedAt = $submission->approved_at; + $originalApprovedBy = $submission->approved_by; + + Livewire::actingAs($user) + ->test(WallOfLoveSubmissionForm::class, ['submission' => $submission]) + ->set('company', 'Updated Company') + ->call('submit'); + + $submission->refresh(); + $this->assertEquals('Updated Company', $submission->company); + $this->assertEquals($originalApprovedAt->toDateTimeString(), $submission->approved_at->toDateTimeString()); + $this->assertEquals($originalApprovedBy, $submission->approved_by); + } + + public function test_edit_mode_only_shows_company_and_photo_fields(): void + { + $user = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->create(['user_id' => $user->id]); + + Livewire::actingAs($user) + ->test(WallOfLoveSubmissionForm::class, ['submission' => $submission]) + ->assertSee('Company') + ->assertSee('Photo') + ->assertDontSee('Name *') + ->assertDontSee('Website or Social Media URL') + ->assertDontSee('Your story or testimonial'); + } + + public function test_owner_can_remove_existing_photo(): void + { + Storage::fake('public'); + + $user = User::factory()->create(); + $submission = WallOfLoveSubmission::factory()->create([ + 'user_id' => $user->id, + 'photo_path' => 'wall-of-love-photos/old-photo.jpg', + ]); + + Livewire::actingAs($user) + ->test(WallOfLoveSubmissionForm::class, ['submission' => $submission]) + ->assertSet('existingPhoto', 'wall-of-love-photos/old-photo.jpg') + ->call('removeExistingPhoto') + ->assertSet('existingPhoto', null) + ->call('submit'); + + $submission->refresh(); + $this->assertNull($submission->photo_path); + } +} From a10a20c422ee7c16e1fb9ce2e363e7642b482c3c Mon Sep 17 00:00:00 2001 From: Simon Hamp Date: Sun, 29 Mar 2026 18:28:17 +0100 Subject: [PATCH 2/2] Stay on edit page after updating Wall of Love listing Show a success callout on the form instead of redirecting to the dashboard, and update the existing photo preview when a new photo is uploaded. Co-Authored-By: Claude Opus 4.6 --- app/Livewire/WallOfLoveSubmissionForm.php | 9 ++++++++- .../livewire/wall-of-love-submission-form.blade.php | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/Livewire/WallOfLoveSubmissionForm.php b/app/Livewire/WallOfLoveSubmissionForm.php index de427e396..10ab9aca8 100644 --- a/app/Livewire/WallOfLoveSubmissionForm.php +++ b/app/Livewire/WallOfLoveSubmissionForm.php @@ -77,7 +77,14 @@ public function submit(): mixed $this->submission->update($data); - return to_route('dashboard')->with('success', 'Your listing has been updated.'); + if ($this->photo) { + $this->existingPhoto = $data['photo_path']; + $this->reset('photo'); + } + + session()->flash('success', 'Your listing has been updated.'); + + return null; } $photoPath = null; diff --git a/resources/views/livewire/wall-of-love-submission-form.blade.php b/resources/views/livewire/wall-of-love-submission-form.blade.php index 24023d342..aa7ea6fd9 100644 --- a/resources/views/livewire/wall-of-love-submission-form.blade.php +++ b/resources/views/livewire/wall-of-love-submission-form.blade.php @@ -1,4 +1,10 @@
+ @if(session()->has('success')) + + {{ session('success') }} + + @endif + @if(! $isEditing) {{-- Name Field --}}