Skip to content

Commit ed4bfca

Browse files
🩷 Improve early adopters page (#221)
* 🌱 Add Wall of Love seeder * πŸ”₯ Remove early adopter card animation * ✨ Enhance Wall of Love component with user image handling and improved layout
1 parent 2feda0b commit ed4bfca

File tree

4 files changed

+153
-47
lines changed

4 files changed

+153
-47
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use Illuminate\Database\Eloquent\Factories\Factory;
6+
7+
/**
8+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\WallOfLoveSubmission>
9+
*/
10+
class WallOfLoveSubmissionFactory extends Factory
11+
{
12+
/**
13+
* Define the model's default state.
14+
*
15+
* @return array<string, mixed>
16+
*/
17+
public function definition(): array
18+
{
19+
return [
20+
'user_id' => \App\Models\User::factory(),
21+
'name' => fake()->name(),
22+
'company' => fake()->optional(0.7)->company(),
23+
'photo_path' => null, // Photos are optional and would need real files
24+
'url' => fake()->optional(0.6)->url(),
25+
'testimonial' => fake()->optional(0.8)->paragraph(3),
26+
'approved_at' => null,
27+
'approved_by' => null,
28+
];
29+
}
30+
31+
/**
32+
* Indicate that the submission is approved.
33+
*/
34+
public function approved(): static
35+
{
36+
return $this->state(fn (array $attributes) => [
37+
'approved_at' => fake()->dateTimeBetween('-30 days', 'now'),
38+
'approved_by' => \App\Models\User::factory(),
39+
]);
40+
}
41+
42+
/**
43+
* Indicate that the submission is pending.
44+
*/
45+
public function pending(): static
46+
{
47+
return $this->state(fn (array $attributes) => [
48+
'approved_at' => null,
49+
'approved_by' => null,
50+
]);
51+
}
52+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace Database\Seeders;
4+
5+
use Illuminate\Database\Seeder;
6+
7+
class WallOfLoveSubmissionSeeder extends Seeder
8+
{
9+
/**
10+
* Run the database seeds.
11+
*/
12+
public function run(): void
13+
{
14+
// Get existing users or create new ones
15+
$existingUsers = \App\Models\User::query()
16+
->whereHas('licenses', function ($query) {
17+
$query->where('created_at', '<', '2025-06-01');
18+
})
19+
->get();
20+
21+
// If we have existing early adopter users, use them
22+
if ($existingUsers->count() >= 5) {
23+
$users = $existingUsers;
24+
} else {
25+
// Create some users with simple licenses (without subscription_item_id)
26+
$users = \App\Models\User::factory()
27+
->count(10)
28+
->create()
29+
->each(function ($user) {
30+
// Give each user an early adopter license (before June 1st, 2025)
31+
\App\Models\License::factory()->create([
32+
'user_id' => $user->id,
33+
'subscription_item_id' => null, // Skip the subscription item relationship
34+
'created_at' => fake()->dateTimeBetween('2024-01-01', '2025-05-31'),
35+
'updated_at' => fake()->dateTimeBetween('2024-01-01', '2025-05-31'),
36+
]);
37+
});
38+
}
39+
40+
// Get or create an admin user for approvals
41+
$admin = \App\Models\User::query()
42+
->where('email', 'admin@example.com')
43+
->first() ?? \App\Models\User::factory()->create([
44+
'name' => 'Admin User',
45+
'email' => 'admin@example.com',
46+
]);
47+
48+
// Create approved submissions (will be displayed on the wall of love page)
49+
\App\Models\WallOfLoveSubmission::factory()
50+
->count(15)
51+
->approved()
52+
->create([
53+
'user_id' => fn () => $users->random()->id,
54+
'approved_by' => $admin->id,
55+
]);
56+
57+
// Create pending submissions (waiting for approval)
58+
\App\Models\WallOfLoveSubmission::factory()
59+
->count(5)
60+
->pending()
61+
->create([
62+
'user_id' => fn () => $users->random()->id,
63+
]);
64+
}
65+
}

β€Žresources/views/components/wall-of-love/early-adopter-card.blade.phpβ€Ž

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
'url' => '',
55
'image' => '',
66
'featured' => false,
7+
'hasUserImage' => false,
78
])
89

910
<article
10-
class="group mt-3 inline-block break-inside-avoid overflow-hidden rounded-2xl text-center opacity-0 transition duration-300 ease-out will-change-transform hover:scale-102 xl:mt-5"
11+
class="group w-full break-inside-avoid overflow-hidden rounded-2xl text-center transition duration-300 ease-out will-change-transform not-last:mb-3 hover:scale-102 xl:not-last:mb-5"
1112
itemscope
1213
>
1314
<figure class="grid">
@@ -32,7 +33,7 @@ class="capitalize transition duration-300 ease-out will-change-transform group-h
3233
</h3>
3334
<p
3435
@class([
35-
'opacity-50',
36+
'truncate opacity-50',
3637
'text-sm' => $featured,
3738
'text-xs' => ! $featured,
3839
])
@@ -53,9 +54,10 @@ class="capitalize transition duration-300 ease-out will-change-transform group-h
5354
decoding="async"
5455
itemprop="image"
5556
@class([
56-
'relative z-10 self-center justify-self-center object-cover brightness-80 transition duration-300 [grid-area:1/-1] group-hover:brightness-100',
57+
'relative z-10 w-full self-center justify-self-center object-cover brightness-80 transition duration-300 [grid-area:1/-1] group-hover:brightness-100',
5758
'aspect-[1/1.3] xl:aspect-[1/1.5]' => $featured,
58-
'aspect-square max-h-50 grayscale group-hover:grayscale-0 xl:max-h-none' => ! $featured,
59+
'aspect-square max-h-50 xl:max-h-none' => ! $featured,
60+
'grayscale-50 dark:brightness-50' => ! $hasUserImage,
5961
])
6062
/>
6163

β€Žresources/views/wall-of-love.blade.phpβ€Ž

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -116,50 +116,32 @@ class="mx-auto mt-5 max-w-2xl text-center text-base/relaxed text-gray-600 sm:tex
116116
->inRandomOrder()
117117
->get();
118118
119+
// Check if any submissions have user-uploaded images
120+
$hasAnyUserImages = $approvedSubmissions->contains(fn ($s) => ! empty($s->photo_path));
121+
119122
// Convert approved submissions to the format expected by the component
120-
$earlyAdopters = $approvedSubmissions->map(function ($submission) {
121-
return [
122-
'name' => $submission->name,
123-
'title' => $submission->company,
124-
'url' => $submission->url,
125-
'image' => $submission->photo_path
126-
? asset('storage/' . $submission->photo_path)
127-
: 'https://avatars.laravel.cloud/' . rand(1, 70) . '?vibe=' . array_rand(['ocean', 'crystal', 'bubble', 'forest', 'sunset']),
128-
'featured' => rand(0, 4) === 0, // Randomly feature about 20% of submissions
129-
'testimonial' => $submission->testimonial,
130-
];
131-
})->toArray();
123+
$earlyAdopters = $approvedSubmissions
124+
->map(function ($submission) use ($hasAnyUserImages) {
125+
$hasUserImage = ! empty($submission->photo_path);
126+
127+
return [
128+
'name' => $submission->name,
129+
'title' => $submission->company,
130+
'url' => $submission->url,
131+
'image' => $hasUserImage
132+
? asset('storage/' . $submission->photo_path)
133+
: 'https://avatars.laravel.cloud/' . rand(1, 70) . '?vibe=' . array_rand(['ocean', 'stealth', 'bubble', 'ice']),
134+
'hasUserImage' => $hasUserImage,
135+
// Only allow featured if has user image (unless no submissions have images)
136+
'featured' => ($hasAnyUserImages ? $hasUserImage : true) && rand(0, 4) === 0,
137+
'testimonial' => $submission->testimonial,
138+
];
139+
})
140+
->toArray();
132141
@endphp
133142

134-
@if(count($earlyAdopters) > 0)
143+
@if (count($earlyAdopters) > 0)
135144
<div
136-
x-init="
137-
() => {
138-
motion.inView($el, (element) => {
139-
const children = Array.from($el.children)
140-
141-
children.forEach((child, i) => {
142-
const range = 20 // px
143-
const xFrom = (Math.random() * 2 - 1) * range
144-
const yFrom = (Math.random() * 2 - 1) * range
145-
146-
motion.animate(
147-
child,
148-
{
149-
x: [xFrom, 0],
150-
y: [yFrom, 0],
151-
opacity: [0, 1],
152-
},
153-
{
154-
duration: 0.7,
155-
ease: motion.backOut,
156-
delay: i * 0.06,
157-
},
158-
)
159-
})
160-
})
161-
}
162-
"
163145
class="relative z-10 mt-10 grid place-items-center 2xs:block 2xs:columns-[10rem] xl:columns-[12rem]"
164146
>
165147
@foreach ($earlyAdopters as $adopter)
@@ -169,14 +151,19 @@ class="relative z-10 mt-10 grid place-items-center 2xs:block 2xs:columns-[10rem]
169151
:url="$adopter['url'] ?? null"
170152
:title="$adopter['title'] ?? null"
171153
:featured="$adopter['featured'] ?? false"
154+
:hasUserImage="$adopter['hasUserImage'] ?? false"
172155
/>
173156
@endforeach
174157
</div>
175158
@else
176159
<div class="relative z-10 mt-10 text-center">
177-
<div class="bg-white dark:bg-gray-800/50 backdrop-blur-sm rounded-2xl p-8 mx-auto max-w-md border border-gray-200 dark:border-gray-700">
178-
<div class="text-6xl mb-4">πŸš€</div>
179-
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-2">
160+
<div
161+
class="mx-auto max-w-md rounded-2xl border border-gray-200 bg-white p-8 backdrop-blur-sm dark:border-gray-700 dark:bg-gray-800/50"
162+
>
163+
<div class="mb-4 text-6xl">πŸš€</div>
164+
<h3
165+
class="mb-2 text-xl font-semibold text-gray-900 dark:text-white"
166+
>
180167
Coming Soon!
181168
</h3>
182169
<p class="text-gray-600 dark:text-gray-400">

0 commit comments

Comments
Β (0)