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;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\StoreUserRequest;
|
||||||
use App\Http\Requests\UpdateUserRequest;
|
use App\Http\Requests\UpdateUserRequest;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\UserService;
|
use App\Services\UserService;
|
||||||
@ -28,6 +29,16 @@ class UsersController extends Controller
|
|||||||
return Inertia::render('dashboard/users/index', compact('users'));
|
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.
|
* 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;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Enums\UserType;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Notifications\ResetPasswordNotification;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class UserService
|
class UserService
|
||||||
{
|
{
|
||||||
@ -34,4 +39,25 @@ class UserService
|
|||||||
User::find($id)->update($data);
|
User::find($id)->update($data);
|
||||||
}, 5);
|
}, 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 TableFooter from '@/components/table/table-footer';
|
||||||
import TableHeader from '@/components/table/table-header';
|
import TableHeader from '@/components/table/table-header';
|
||||||
import { Card } from '@/components/ui/card';
|
import { Card } from '@/components/ui/card';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table';
|
||||||
import DashboardLayout from '@/layouts/dashboard/layout';
|
import DashboardLayout from '@/layouts/dashboard/layout';
|
||||||
import { SharedData } from '@/types/global';
|
import { SharedData } from '@/types/global';
|
||||||
@ -9,6 +10,8 @@ import { SortingState, flexRender, getCoreRowModel, getFilteredRowModel, getSort
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import TableColumn from './Partials/table-columns';
|
import TableColumn from './Partials/table-columns';
|
||||||
|
import InviteForm from './Partials/invite-form';
|
||||||
|
import { Plus } from 'lucide-react';
|
||||||
|
|
||||||
interface Props extends SharedData {
|
interface Props extends SharedData {
|
||||||
users: Pagination<User>;
|
users: Pagination<User>;
|
||||||
@ -31,10 +34,20 @@ const Index = (props: Props) => {
|
|||||||
<Card>
|
<Card>
|
||||||
<TableFilter
|
<TableFilter
|
||||||
data={props.users}
|
data={props.users}
|
||||||
title="User List"
|
title={props.translate.dashboard.user_list}
|
||||||
globalSearch={true}
|
globalSearch={true}
|
||||||
tablePageSizes={[10, 15, 20, 25]}
|
tablePageSizes={[10, 15, 20, 25]}
|
||||||
routeName="users.index"
|
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" />}
|
// Icon={<Users className="h-6 w-6 text-primary" />}
|
||||||
// exportPath={route('users.export')}
|
// 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
|
// Users
|
||||||
user_role: string;
|
user_role: string;
|
||||||
provide_essential_user_details: string;
|
provide_essential_user_details: string;
|
||||||
|
invite_user: string;
|
||||||
|
invite_user_description: string;
|
||||||
|
|
||||||
// Content Management
|
// Content Management
|
||||||
media_library: string;
|
media_library: string;
|
||||||
|
|||||||
@ -29,6 +29,7 @@ use App\Http\Controllers\UsersController;
|
|||||||
Route::prefix('dashboard')->group(function () {
|
Route::prefix('dashboard')->group(function () {
|
||||||
// users
|
// users
|
||||||
Route::resource('users', UsersController::class)->only(['index', 'update']);
|
Route::resource('users', UsersController::class)->only(['index', 'update']);
|
||||||
|
Route::post('users', [UsersController::class, 'store'])->name('users.store')->middleware('smtpConfig', 'checkSmtp');
|
||||||
|
|
||||||
// Category
|
// Category
|
||||||
Route::resource('courses/categories', CourseCategoryController::class)->only(['index', 'store', 'destroy'])->names('categories');
|
Route::resource('courses/categories', CourseCategoryController::class)->only(['index', 'store', 'destroy'])->names('categories');
|
||||||
|
|||||||
@ -250,6 +250,9 @@ return [
|
|||||||
'user_preferences' => 'User Preferences',
|
'user_preferences' => 'User Preferences',
|
||||||
'update_user' => 'Update User',
|
'update_user' => 'Update User',
|
||||||
'select_approval_status' => 'Select the approval status',
|
'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' => 'Course Progress',
|
'course_progress' => 'Course Progress',
|
||||||
|
|||||||
@ -393,6 +393,9 @@ return [
|
|||||||
'user_preferences' => 'User Preferences',
|
'user_preferences' => 'User Preferences',
|
||||||
'update_user' => 'Update User',
|
'update_user' => 'Update User',
|
||||||
'select_approval_status' => 'Select the approval status',
|
'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