Skip to content

Commit a051ee9

Browse files
committed
fix types/lint/format
1 parent 9080cac commit a051ee9

File tree

8 files changed

+130
-62
lines changed

8 files changed

+130
-62
lines changed

app/Actions/OptimizeWebpImageAction.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Actions;
44

5-
use Illuminate\Http\UploadedFile;
65
use Intervention\Image\Laravel\Facades\Image;
76
use Illuminate\Support\Str;
87

app/Http/Controllers/PuppyController.php

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
namespace App\Http\Controllers;
44

5+
use App\Actions\OptimizeWebpImageAction;
56
use App\Http\Resources\PuppyResource;
67
use App\Models\Puppy;
78
use Illuminate\Http\Request;
89
use Illuminate\Support\Facades\Storage;
910
use Inertia\Inertia;
10-
use Intervention\Image\Laravel\Facades\Image;
11-
use Illuminate\Support\Str;
1211

1312
class PuppyController extends Controller
1413
{
@@ -45,7 +44,6 @@ public function index(Request $request)
4544
// ------------------------------
4645
public function like(Request $request, Puppy $puppy)
4746
{
48-
usleep(200000);
4947
$puppy->likedBy()->toggle($request->user()->id);
5048
return back();
5149
}
@@ -55,7 +53,6 @@ public function like(Request $request, Puppy $puppy)
5553
// ------------------------------
5654
public function store(Request $request)
5755
{
58-
usleep(200000);
5956
// Validate the data
6057
$request->validate([
6158
'name' => 'required|string|max:255',
@@ -67,20 +64,10 @@ public function store(Request $request)
6764
$image_url = null;
6865
if ($request->hasFile('image')) {
6966

70-
// Image optimization
71-
$image = Image::read($request->file('image'));
67+
$optimized = (new OptimizeWebpImageAction())->handle($request->file('image'));
68+
$path = 'puppies/' . $optimized['fileName'];
7269

73-
// Scale down only
74-
if ($image->width() > 1000) {
75-
$image->scale(width: 1000);
76-
}
77-
78-
$webpEncoded = $image->toWebp(quality: 95)->toString();
79-
80-
$fileName = Str::random() . '.webp';
81-
$path = 'puppies/' . $fileName;
82-
83-
$stored = Storage::disk('public')->put($path, $webpEncoded);
70+
$stored = Storage::disk('public')->put($path, $optimized['webpString']);
8471

8572
if (!$stored) {
8673
return back()->withErrors(['image' => 'Failed to upload image.']);
@@ -104,8 +91,6 @@ public function store(Request $request)
10491
// ------------------------------
10592
public function destroy(Request $request, Puppy $puppy)
10693
{
107-
sleep(2);
108-
10994
$imagePath = str_replace('/storage/', '', $puppy->image_url);
11095

11196
if ($request->user()->cannot('delete', $puppy)) {
@@ -129,6 +114,47 @@ public function destroy(Request $request, Puppy $puppy)
129114
// ------------------------------
130115
public function update(Request $request, Puppy $puppy)
131116
{
132-
dd('Hello from the update method!');
117+
// Validate the data
118+
$request->validate([
119+
'name' => 'required|string|max:255',
120+
'trait' => 'required|string|max:255',
121+
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:5120',
122+
]);
123+
124+
// If there is a new image
125+
if ($request->hasFile('image')) {
126+
127+
$oldImagePath = str_replace('/storage/', '', $puppy->image_url);
128+
129+
// Optimize and store the new image
130+
$optimized = (new OptimizeWebpImageAction())->handle($request->file('image'));
131+
$path = 'puppies/' . $optimized['fileName'];
132+
133+
$stored = Storage::disk('public')->put($path, $optimized['webpString']);
134+
135+
if (!$stored) {
136+
return back()->withErrors(['image' => 'Failed to upload image.']);
137+
}
138+
$puppy->image_url = Storage::url($path);
139+
140+
// Delete the old image
141+
if ($oldImagePath && Storage::disk('public')->exists($oldImagePath)) {
142+
Storage::disk('public')->delete($oldImagePath);
143+
}
144+
}
145+
146+
// Update the puppy values
147+
$puppy->name = $request->name;
148+
$puppy->trait = $request->trait;
149+
150+
151+
$puppy->save();
152+
153+
return back()
154+
->with('success', 'Puppy updated successfully!');
155+
156+
// Save the updated puppy
157+
158+
// Redirect back with success message
133159
}
134160
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export function Container({ children }) {
2-
return <div className="mx-auto max-w-5xl p-4 md:p-8">{children}</div>;
1+
export function Container({ children }: { children: React.ReactNode }) {
2+
return <div className="mx-auto max-w-5xl p-4 md:p-8">{children}</div>;
33
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { cn } from '@/lib/utils';
2+
import { useEffect, useState } from 'react';
3+
4+
export function ImageUploadPreview({
5+
source,
6+
className,
7+
...restProps
8+
}: {
9+
source: File | string | null;
10+
className?: string;
11+
} & React.ImgHTMLAttributes<HTMLImageElement>) {
12+
const [src, setSrc] = useState<string | null>(null);
13+
14+
useEffect(() => {
15+
if (source instanceof File) {
16+
const objectUrl = URL.createObjectURL(source);
17+
setSrc(objectUrl);
18+
return () => {
19+
URL.revokeObjectURL(objectUrl);
20+
};
21+
} else {
22+
setSrc(source);
23+
}
24+
}, [source]);
25+
26+
if (!src) return null;
27+
28+
return <img src={src} className={cn('mt-4 h-24 rounded-md', className)} {...restProps} />;
29+
}

resources/js/components/NewPuppyForm.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useForm } from '@inertiajs/react';
22
import { useRef } from 'react';
3+
import { ImageUploadPreview } from './ImageUploadPreview';
34

45
export function NewPuppyForm({ mainRef }: { mainRef?: React.RefObject<HTMLElement | null> }) {
56
const { post, setData, data, errors, reset, processing } = useForm({
@@ -65,6 +66,7 @@ export function NewPuppyForm({ mainRef }: { mainRef?: React.RefObject<HTMLElemen
6566
onChange={(e) => setData('image', e.target.files ? e.target.files[0] : null)}
6667
/>
6768
{errors.image && <p className="mt-1 text-xs text-red-500">{errors.image}</p>}
69+
<ImageUploadPreview height={96} className="self-start" source={data.image} />
6870
</fieldset>
6971
</div>
7072
<button

resources/js/components/Shortlist.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { useForm, usePage } from '@inertiajs/react';
1+
import { useForm } from '@inertiajs/react';
22
import { Heart, LoaderCircle, X } from 'lucide-react';
3-
import { Puppy, SharedData } from '../types';
3+
import { Puppy } from '../types';
44

5-
// TODO: Make sure all the liked puppies are showing, not just the ones from the current page.
65
export function Shortlist({ puppies }: { puppies: Puppy[] }) {
7-
const { auth } = usePage<SharedData>().props;
86
const firstFivePuppies = puppies.slice(0, 5);
97
const extraPuppiesCount = puppies.length - firstFivePuppies.length;
108

resources/js/components/puppy-update.tsx

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { useForm } from '@inertiajs/react';
44
import clsx from 'clsx';
55
import { EditIcon, LoaderCircle } from 'lucide-react';
66
import { useState } from 'react';
7+
import { ImageUploadPreview } from './ImageUploadPreview';
78
import { Button } from './ui/button';
89
import { Input } from './ui/input';
910
import { Label } from './ui/label';
1011

1112
export function PuppyUpdate({ puppy }: { puppy: Puppy }) {
12-
const { data, setData, errors, put, processing } = useForm({
13+
const { data, setData, transform, errors, post, processing } = useForm({
1314
name: puppy.name,
1415
trait: puppy.trait,
1516
image: null as File | null,
@@ -28,47 +29,60 @@ export function PuppyUpdate({ puppy }: { puppy: Puppy }) {
2829
<DialogTitle>Edit {puppy.name}</DialogTitle>
2930
<DialogDescription>Make changes to your puppy’s information below.</DialogDescription>
3031
<form
31-
className="space-y-6"
32+
className="flex flex-col gap-6"
3233
onSubmit={(e) => {
3334
e.preventDefault();
34-
// TODO
35-
put(route('puppies.update', puppy.id));
35+
transform((data) => ({
36+
...data,
37+
_method: 'put',
38+
}));
39+
post(route('puppies.update', puppy.id), {
40+
preserveScroll: true,
41+
onSuccess: () => setOpen(false),
42+
});
3643
}}
3744
>
38-
<Label htmlFor="name">Name</Label>
39-
<Input
40-
id="name"
41-
className="mt-1 block w-full"
42-
value={data.name}
43-
onChange={(e) => setData('name', e.target.value)}
44-
required
45-
autoComplete="name"
46-
placeholder="Full name"
47-
/>
48-
{errors.name && <p className="mt-1 text-xs text-red-500">{errors.name}</p>}
45+
<fieldset>
46+
<Label htmlFor="name">Name</Label>
47+
<Input
48+
id="name"
49+
className="mt-1 block w-full"
50+
value={data.name}
51+
onChange={(e) => setData('name', e.target.value)}
52+
required
53+
autoComplete="name"
54+
placeholder="Full name"
55+
/>
56+
{errors.name && <p className="mt-1 text-xs text-red-500">{errors.name}</p>}
57+
</fieldset>
4958

50-
<Label htmlFor="trait">Personality trait</Label>
51-
<Input
52-
id="trait"
53-
className="mt-1 block w-full"
54-
value={data.trait}
55-
onChange={(e) => setData('trait', e.target.value)}
56-
required
57-
placeholder="Personality trait"
58-
/>
59+
<fieldset>
60+
<Label htmlFor="trait">Personality trait</Label>
61+
<Input
62+
id="trait"
63+
className="mt-1 block w-full"
64+
value={data.trait}
65+
onChange={(e) => setData('trait', e.target.value)}
66+
required
67+
placeholder="Personality trait"
68+
/>
69+
{errors.trait && <p className="mt-1 text-xs text-red-500">{errors.trait}</p>}
70+
</fieldset>
5971

60-
{errors.trait && <p className="mt-1 text-xs text-red-500">{errors.trait}</p>}
72+
<fieldset>
73+
<Label htmlFor="image">Change image</Label>
74+
<Input
75+
id="image"
76+
type="file"
77+
className="mt-1 block w-full"
78+
onChange={(e) => setData('image', e.target.files ? e.target.files[0] : null)}
79+
placeholder="Profile picture"
80+
/>
6181

62-
<Label htmlFor="image">Change image</Label>
63-
<Input
64-
id="image"
65-
type="file"
66-
className="mt-1 block w-full"
67-
onChange={(e) => setData('image', e.target.files ? e.target.files[0] : null)}
68-
placeholder="Profile picture"
69-
/>
82+
{errors.image && <p className="mt-1 text-xs text-red-500">{errors.image}</p>}
7083

71-
{errors.image && <p className="mt-1 text-xs text-red-500">{errors.image}</p>}
84+
<ImageUploadPreview source={data.image ?? puppy.imageUrl} />
85+
</fieldset>
7286

7387
<DialogFooter className="gap-2">
7488
<DialogClose asChild>

resources/js/pages/dashboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default function Dashboard() {
1414
return (
1515
<AppLayout breadcrumbs={breadcrumbs}>
1616
<Head title="Dashboard" />
17-
<div className="flex h-full flex-1 flex-col gap-4 rounded-xl p-4 overflow-x-auto">
17+
<div className="flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4">
1818
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
1919
<div className="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 dark:border-sidebar-border">
2020
<PlaceholderPattern className="absolute inset-0 size-full stroke-neutral-900/20 dark:stroke-neutral-100/20" />

0 commit comments

Comments
 (0)