added add user function
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m49s
All checks were successful
Build & Push Docker Image / docker (push) Successful in 1m49s
This commit is contained in:
parent
731e226a9f
commit
6fba63daf5
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreUserRequest;
|
||||
use App\Http\Requests\UpdateUserRequest;
|
||||
use App\Models\User;
|
||||
use App\Services\UserService;
|
||||
@ -28,6 +29,16 @@ class UsersController extends Controller
|
||||
return Inertia::render('dashboard/users/index', compact('users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created user and send an invite.
|
||||
*/
|
||||
public function store(StoreUserRequest $request): RedirectResponse
|
||||
{
|
||||
$this->userService->inviteUser($request->validated());
|
||||
|
||||
return redirect()->back()->with('success', __('dashboard.user_invited'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's account.
|
||||
*/
|
||||
|
||||
30
app/Http/Requests/StoreUserRequest.php
Normal file
30
app/Http/Requests/StoreUserRequest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreUserRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:users,email'],
|
||||
'status' => ['required', 'integer', 'in:0,1'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,15 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Enums\UserType;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ResetPasswordNotification;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UserService
|
||||
{
|
||||
@ -34,4 +39,25 @@ class UserService
|
||||
User::find($id)->update($data);
|
||||
}, 5);
|
||||
}
|
||||
|
||||
public function inviteUser(array $data): User
|
||||
{
|
||||
return DB::transaction(function () use ($data) {
|
||||
$user = User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'role' => UserType::STUDENT->value,
|
||||
'status' => $data['status'] ?? 1,
|
||||
'password' => Hash::make(Str::random(32)),
|
||||
]);
|
||||
|
||||
$token = Password::createToken($user);
|
||||
|
||||
DB::afterCommit(function () use ($user, $token) {
|
||||
$user->notify(new ResetPasswordNotification($token));
|
||||
});
|
||||
|
||||
return $user;
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
|
||||
83
resources/js/pages/dashboard/users/Partials/invite-form.tsx
Normal file
83
resources/js/pages/dashboard/users/Partials/invite-form.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import InputError from '@/components/input-error';
|
||||
import LoadingButton from '@/components/loading-button';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { SharedData } from '@/types/global';
|
||||
import { useForm, usePage } from '@inertiajs/react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
actionComponent: ReactNode;
|
||||
}
|
||||
|
||||
const InviteForm = ({ actionComponent }: Props) => {
|
||||
const { props } = usePage<SharedData>();
|
||||
const { translate } = props;
|
||||
const { dashboard, input, common, button } = translate;
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { data, post, setData, processing, errors, reset } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
status: 1,
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
post(route('users.store'), {
|
||||
onSuccess: () => {
|
||||
reset();
|
||||
setOpen(false);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>{actionComponent}</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{dashboard.invite_user}</DialogTitle>
|
||||
<p className="text-muted-foreground text-sm">{dashboard.invite_user_description}</p>
|
||||
</DialogHeader>
|
||||
|
||||
<form onSubmit={handleSubmit} className="mt-4 space-y-4 text-start">
|
||||
<div>
|
||||
<Label>{input.name}</Label>
|
||||
<Input required value={data.name} onChange={(e) => setData('name', e.target.value)} />
|
||||
<InputError message={errors.name} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{input.email}</Label>
|
||||
<Input type="email" required value={data.email} onChange={(e) => setData('email', e.target.value)} />
|
||||
<InputError message={errors.email} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label>{input.status}</Label>
|
||||
<Select value={data.status === 1 ? 'active' : 'inactive'} onValueChange={(value) => setData('status', value === 'active' ? 1 : 0)}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={dashboard.select_approval_status} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="active">{common.active}</SelectItem>
|
||||
<SelectItem value="inactive">{common.inactive}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputError message={errors.status} />
|
||||
</div>
|
||||
|
||||
<LoadingButton loading={processing} className="w-full">
|
||||
{button.send ?? button.submit}
|
||||
</LoadingButton>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default InviteForm;
|
||||
@ -2,6 +2,7 @@ import TableFilter from '@/components/table/table-filter';
|
||||
import TableFooter from '@/components/table/table-footer';
|
||||
import TableHeader from '@/components/table/table-header';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
|
||||
import DashboardLayout from '@/layouts/dashboard/layout';
|
||||
import { SharedData } from '@/types/global';
|
||||
@ -9,6 +10,8 @@ import { SortingState, flexRender, getCoreRowModel, getFilteredRowModel, getSort
|
||||
import * as React from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import TableColumn from './Partials/table-columns';
|
||||
import InviteForm from './Partials/invite-form';
|
||||
import { Plus } from 'lucide-react';
|
||||
|
||||
interface Props extends SharedData {
|
||||
users: Pagination<User>;
|
||||
@ -31,10 +34,20 @@ const Index = (props: Props) => {
|
||||
<Card>
|
||||
<TableFilter
|
||||
data={props.users}
|
||||
title="User List"
|
||||
title={props.translate.dashboard.user_list}
|
||||
globalSearch={true}
|
||||
tablePageSizes={[10, 15, 20, 25]}
|
||||
routeName="users.index"
|
||||
component={
|
||||
<InviteForm
|
||||
actionComponent={
|
||||
<Button size="sm">
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{props.translate.dashboard.invite_user}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
}
|
||||
// Icon={<Users className="h-6 w-6 text-primary" />}
|
||||
// exportPath={route('users.export')}
|
||||
/>
|
||||
|
||||
2
resources/js/types/lang/dashboard.d.ts
vendored
2
resources/js/types/lang/dashboard.d.ts
vendored
@ -153,6 +153,8 @@ interface DashboardLang {
|
||||
// Users
|
||||
user_role: string;
|
||||
provide_essential_user_details: string;
|
||||
invite_user: string;
|
||||
invite_user_description: string;
|
||||
|
||||
// Content Management
|
||||
media_library: string;
|
||||
|
||||
@ -29,6 +29,7 @@ use App\Http\Controllers\UsersController;
|
||||
Route::prefix('dashboard')->group(function () {
|
||||
// users
|
||||
Route::resource('users', UsersController::class)->only(['index', 'update']);
|
||||
Route::post('users', [UsersController::class, 'store'])->name('users.store')->middleware('smtpConfig', 'checkSmtp');
|
||||
|
||||
// Category
|
||||
Route::resource('courses/categories', CourseCategoryController::class)->only(['index', 'store', 'destroy'])->names('categories');
|
||||
|
||||
@ -250,6 +250,9 @@ return [
|
||||
'user_preferences' => 'User Preferences',
|
||||
'update_user' => 'Update User',
|
||||
'select_approval_status' => 'Select the approval status',
|
||||
'invite_user' => 'Invite User',
|
||||
'invite_user_description' => 'Add a user and email them a password reset link to join.',
|
||||
'user_invited' => 'User invited successfully. A password reset link has been sent.',
|
||||
|
||||
// Course Progress
|
||||
'course_progress' => 'Course Progress',
|
||||
|
||||
@ -393,6 +393,9 @@ return [
|
||||
'user_preferences' => 'User Preferences',
|
||||
'update_user' => 'Update User',
|
||||
'select_approval_status' => 'Select the approval status',
|
||||
'invite_user' => 'Invite User',
|
||||
'invite_user_description' => 'Add a user and email them a password reset link to join.',
|
||||
'user_invited' => 'User invited successfully. A password reset link has been sent.',
|
||||
]
|
||||
],
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user