Skip to content

Commit

Permalink
(ui): add user edit page
Browse files Browse the repository at this point in the history
  • Loading branch information
camwhit-e committed Oct 24, 2024
1 parent f580850 commit 3032d12
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 106 deletions.
50 changes: 48 additions & 2 deletions app/Http/Controllers/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@
use Inertia\Response;
use Influx\Models\User;
use Illuminate\Http\Request;
use Illuminate\Contracts\Hashing\Hasher;

class UsersController extends Controller
{
/**
* UsersController constructor.
*/
public function __construct(private Hasher $hasher)
{
}

/**
* Display the table holding all user accounts.
*/
Expand Down Expand Up @@ -39,10 +47,48 @@ public function store(Request $request): Response
'superuser' => ['required'],
]);

$validated['superuser'] = $validated['superuser'] === 'on' ? true : false;
$validated['password'] = $this->hasher->make($validated['password']);

$user = User::create($validated);

return Inertia::render('Users/Edit', ['user' => $user]);
}

/**
* View a user already on the system.
*/
public function view(Request $request, int $id): Response
{
return Inertia::render('Users/Edit', [
'user' => User::findOrFail($id),
]);
}

/**
* Update the details of an existing user.
*/
public function update(Request $request, int $id): Response
{
$user = User::findOrFail($id);

$validated = $request->validate([
'name' => ['required', 'max:50'],
'email' => ['required', 'max:50', 'email'],
'password' => ['nullable', 'min:8'],
'superuser' => ['required'],
]);

if (!empty($validated['password'])) {
$validated['password'] = $this->hasher->make($validated['password']);
} else {
unset($validated['password']);
}

$validated['superuser'] = $validated['superuser'] === 'on' ? true : false;

User::create($validated);
$user->forceFill($validated)->saveOrFail();

return Inertia::render('Users/Index');
return Inertia::render('Users/Edit', ['user' => $user]);
}
}
33 changes: 33 additions & 0 deletions resources/js/Pages/Users/Edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Card from '@/Components/Card';
import Authenticated from '@/Layouts/AuthenticatedLayout';
import { PageProps, User } from '@/types';
import Avatar from 'boring-avatars';
import UserForm from './UserForm';

export default function Edit({ user }: PageProps<{ user: User }>) {
if (!user) return <></>;

return (
<Authenticated title={'Edit user: ' + user.name}>
<Card
header={'Edit User Account'}
description={
'Update account details or remove the account from the system.'
}
>
<div className={'grid gap-6 lg:grid-cols-3'}>
<div className={'grid items-center justify-center'}>
<div>
<Avatar
name={user.name}
className={'h-24'}
variant={'beam'}
/>
</div>
</div>
<UserForm user={user} />
</div>
</Card>
</Authenticated>
);
}
6 changes: 3 additions & 3 deletions resources/js/Pages/Users/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function Index({ users }: PageProps<{ users: User[] }>) {
>
<Avatar
variant={'beam'}
name={'EmailName'}
name={user.name}
className={'h-10 w-10'}
/>
<div className="ps-3">
Expand All @@ -49,12 +49,12 @@ export default function Index({ users }: PageProps<{ users: User[] }>) {
</div>
</td>
<td className="px-6 py-4">
<a
<Link
href={`/users/${user.id}`}
className="font-medium text-blue-600 hover:underline dark:text-blue-500"
>
Edit
</a>
</Link>
</td>
</tr>
))}
Expand Down
103 changes: 2 additions & 101 deletions resources/js/Pages/Users/New.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,9 @@
import Card from '@/Components/Card';
import PrimaryButton from '@/Components/PrimaryButton';
import Authenticated from '@/Layouts/AuthenticatedLayout';
import { router } from '@inertiajs/react';
import Avatar from 'boring-avatars';
import { useState } from 'react';
import UserForm from './UserForm';

export default function New() {
const [values, setValues] = useState({
email: '',
name: '',
password: '',
superuser: false,
});

function handleChange(e: {
target: { id: string; value: string | boolean };
}) {
const key = e.target.id;
const value = e.target.value;
setValues((values) => ({
...values,
[key]: value,
}));
}

function handleSubmit(e: { preventDefault: () => void }) {
e.preventDefault();
router.post('/users/new', values);
}

return (
<Authenticated title={'All Users'}>
<Card
Expand All @@ -45,81 +20,7 @@ export default function New() {
/>
</div>
</div>
<form
className="lg:col-span-2"
autoComplete={'off'}
onSubmit={handleSubmit}
>
<div className="group relative z-0 mb-5 w-full">
<input
type="email"
name="email"
id="email"
onChange={handleChange}
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
required
/>
<label className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 dark:text-gray-400 peer-focus:dark:text-green-500">
Email address
</label>
</div>
<div className="group relative z-0 mb-5 w-full">
<input
type="password"
name="password"
id="password"
onChange={handleChange}
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
required
/>
<label
htmlFor="password"
className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 rtl:peer-focus:translate-x-1/4 dark:text-gray-400 peer-focus:dark:text-green-500"
>
Password
</label>
</div>
<div className="grid md:grid-cols-2 md:gap-6">
<div className="group relative z-0 mb-5 w-full">
<input
type="text"
name="name"
id="name"
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
onChange={handleChange}
required
/>
<label
htmlFor="name"
className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 rtl:peer-focus:translate-x-1/4 dark:text-gray-400 peer-focus:dark:text-green-500"
>
name
</label>
</div>
<div className="mb-4 flex items-center">
<input
id="superuser"
type="checkbox"
onChange={handleChange}
className="h-4 w-4 rounded border-gray-300 bg-gray-100 text-green-600 focus:ring-2 focus:ring-green-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-green-600 dark:focus:ring-offset-gray-800"
/>
<label
htmlFor="superuser"
className="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Grant this account Superuser permissions
</label>
</div>
</div>
<div className={'text-right'}>
<PrimaryButton type={'submit'}>
Submit
</PrimaryButton>
</div>
</form>
<UserForm />
</div>
</Card>
</Authenticated>
Expand Down
113 changes: 113 additions & 0 deletions resources/js/Pages/Users/UserForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import PrimaryButton from '@/Components/PrimaryButton';
import { User } from '@/types';
import { router } from '@inertiajs/react';
import { useState } from 'react';

export default function UserForm({ user }: { user?: User }) {
const [values, setValues] = useState({
email: user?.email ?? '',
name: user?.name ?? '',
password: '',
superuser: user?.superuser ?? false,
});

function handleChange(e: {
target: { id: string; value: string | boolean };
}) {
const key = e.target.id;
const value = e.target.value;
setValues((values) => ({
...values,
[key]: value,
}));
}

function handleSubmit(e: { preventDefault: () => void }) {
e.preventDefault();

if (user) {
router.put(`/users/${user!.id}`, values);
} else {
router.post('/users/new', values);
}
}

return (
<form
className="lg:col-span-2"
autoComplete={'off'}
onSubmit={handleSubmit}
>
<div className="group relative z-0 mb-5 w-full">
<input
type="email"
name="email"
id="email"
onChange={handleChange}
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
defaultValue={user?.email}
required
/>
<label className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 dark:text-gray-400 peer-focus:dark:text-green-500">
Email address
</label>
</div>
<div className="group relative z-0 mb-5 w-full">
<input
type="password"
name="password"
id="password"
onChange={handleChange}
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
required={!user}
/>
<label
htmlFor="password"
className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 rtl:peer-focus:translate-x-1/4 dark:text-gray-400 peer-focus:dark:text-green-500"
>
Password
</label>
</div>
<div className="grid md:grid-cols-2 md:gap-6">
<div className="group relative z-0 mb-5 w-full">
<input
type="text"
name="name"
id="name"
defaultValue={user?.name}
className="peer block w-full appearance-none border-0 border-b-2 border-gray-300 bg-transparent px-0 py-2.5 text-sm text-gray-900 focus:border-green-600 focus:outline-none focus:ring-0 dark:border-gray-600 dark:text-white dark:focus:border-green-500"
placeholder=" "
onChange={handleChange}
required
/>
<label
htmlFor="name"
className="absolute top-3 -z-10 origin-[0] -translate-y-6 scale-75 transform text-sm text-gray-500 duration-300 peer-placeholder-shown:translate-y-0 peer-placeholder-shown:scale-100 peer-focus:start-0 peer-focus:-translate-y-6 peer-focus:scale-75 peer-focus:font-medium peer-focus:text-green-600 rtl:peer-focus:translate-x-1/4 dark:text-gray-400 peer-focus:dark:text-green-500"
>
Username
</label>
</div>
<div className="mb-4 flex items-center">
<input
id="superuser"
type="checkbox"
defaultChecked={user?.superuser}
onChange={handleChange}
className="h-4 w-4 rounded border-gray-300 bg-gray-100 text-green-600 focus:ring-2 focus:ring-green-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-green-600 dark:focus:ring-offset-gray-800"
/>
<label
htmlFor="superuser"
className="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>
Grant this account Superuser permissions
</label>
</div>
</div>
<div className={'text-right'}>
<PrimaryButton type={'submit'}>Submit</PrimaryButton>
</div>
</form>
);
}
4 changes: 4 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
Route::middleware('auth')->group(function () {
Route::prefix('/users')->group(function () {
Route::get('/', [UsersController::class, 'index'])->name('users.index');

Route::get('/new', [UsersController::class, 'new'])->name('users.new');
Route::post('/new', [UsersController::class, 'store'])->name('users.store');

Route::get('/{user:id}', [UsersController::class, 'view'])->name('users.view');
Route::put('/{user:id}', [UsersController::class, 'update'])->name('users.update');
});

Route::prefix('/profile')->group(function () {
Expand Down

0 comments on commit 3032d12

Please sign in to comment.