registration updated

This commit is contained in:
Thekindbull 2024-12-15 23:36:22 +08:00
parent d6eccdfe6e
commit bbfba9e593
25 changed files with 676 additions and 55 deletions

View File

@ -10,6 +10,9 @@
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use App\Notifications\UserRegistered;
class RegisterController extends Controller class RegisterController extends Controller
{ {
@ -55,7 +58,6 @@ protected function validator(array $data)
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'phone' => ['required', 'string', 'unique:users'], 'phone' => ['required', 'string', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'inn' => ['required', 'unique:companies'] 'inn' => ['required', 'unique:companies']
]); ]);
} }
@ -68,12 +70,14 @@ protected function validator(array $data)
*/ */
protected function create(array $data) protected function create(array $data)
{ {
$newUserPassword = Str::password(8);
$user = User::create([ $user = User::create([
'name' => $data['name'], 'name' => $data['name'],
'email' => $data['email'], 'email' => $data['email'],
'phone' => $data['phone'], 'phone' => $data['phone'],
'password' => Hash::make($data['password']), 'password' => Hash::make($newUserPassword),
]); ]);
$user->notify(new UserRegistered(login: $user->email, password: $newUserPassword));
$company = Company::create([ $company = Company::create([
'name' => $data['name'], 'name' => $data['name'],

View File

@ -4,11 +4,13 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use App\Models\Company\Company; use App\Models\Company\Company;
use App\Models\Company\CompanyAdmin; use App\Models\Company\CompanyAdmin;
use App\Models\User; use App\Models\User;
use App\Notifications\UserRegistered;
use Illuminate\Support\Str;
class ConfirmCompanyController extends Controller class ConfirmCompanyController extends Controller
{ {
@ -27,12 +29,14 @@ public function __invoke(Request $request)
} }
else else
{ {
$newUserPassword = Str::password(8);
$user = User::create([ $user = User::create([
'name' => $request->user_name, 'name' => $request->user_name,
'email' => $company->email, 'email' => $company->email,
'phone' => $request->user_phone, 'phone' => $request->user_phone,
'password' => uniqid(), 'password' => Hash::make($newUserPassword),
]); ]);
$user->notify(new UserRegistered($user->email, password: $newUserPassword));
} }
CompanyAdmin::create([ CompanyAdmin::create([
'user_id' => $user->id, 'user_id' => $user->id,

View File

@ -7,52 +7,67 @@
use App\Models\Company\Company; use App\Models\Company\Company;
use App\Models\Company\Details; use App\Models\Company\Details;
use App\Models\Agent; use App\Models\Agent\Agent;
class DetailsController extends Controller class DetailsController extends Controller
{ {
public function index() { public function index()
{
$company = false; $company = false;
$userId = auth()->user()->id; $userId = auth()->user()->id;
$agent = Agent::where('user_id', $userId)->get(); $agent = Agent::where('user_id', $userId)->get();
if ($agent->count() == 1) { if ($agent->count() == 1)
{
$agent = $agent->first(); $agent = $agent->first();
$company = Company::find($agent->company_id); $company = Company::find($agent->company_id);
} else { }
else
{
return back(); return back();
}; }
;
$details = new Details($company); $details = new Details($company);
$details = $details->details; $details = $details->details;
if ($company->type == 'SELFEMP') { if ($company->type == 'SELFEMP')
{
return view('company.details.selfemp', [ return view('company.details.selfemp', [
'company' => $company, 'company' => $company,
'details' => $details 'details' => $details
]); ]);
}; }
if ($company->type == 'AGENCY') { ;
if ($company->type == 'AGENCY')
{
return view('company.details.agency', [ return view('company.details.agency', [
'company' => $company, 'company' => $company,
'details' => $details 'details' => $details
]); ]);
}; }
} ;
public function store(Request $request, Company $company) { }
public function store(Request $request, Company $company)
{
$userId = auth()->user()->id; $userId = auth()->user()->id;
$agent = Agent::where('user_id', $userId)->get(); $agent = Agent::where('user_id', $userId)->get();
if ($agent->count() == 1) { if ($agent->count() == 1)
{
$agent = $agent->first(); $agent = $agent->first();
if ($agent->company_id != $company->id) { if ($agent->company_id != $company->id)
{
return; return;
}
} }
} else { else
{
return back(); return back();
}; }
;
$company->details = $request->all(); $company->details = $request->all();
$company->save(); $company->save();
return to_route('company.details', [ return to_route('company.details', [
'company' => $company 'company' => $company
]); ]);
}
} }
}

View File

@ -2,14 +2,13 @@
namespace App\Models; namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens; use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
class User extends Authenticatable implements MustVerifyEmail {
{
use HasApiTokens, HasFactory, Notifiable; use HasApiTokens, HasFactory, Notifiable;
/** /**
@ -44,12 +43,13 @@ class User extends Authenticatable implements MustVerifyEmail
'password' => 'hashed', 'password' => 'hashed',
]; ];
public function getPartialsName() { public function getPartialsName()
{
$name = explode(' ', $this->name); $name = explode(' ', $this->name);
return [ return [
'firstName' => $name[0], 'firstName' => $name[0],
'secondName' => $name[1], 'secondName' => $name[1],
'familyName' => $name[2] 'familyName' => $name[2]
]; ];
}
} }
}

View File

@ -0,0 +1,59 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\HtmlString;
class UserRegistered extends Notification
{
use Queueable;
public $password;
public $login;
/**
* Create a new notification instance.
*/
public function __construct(string $login, string $password)
{
$this->password = $password;
$this->login = $login;
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Account was created'))
->line(__('Your account was created'))
->line(__('Your login', ['login' => $this->login]))
->line(__('Your password', ['password' => $this->password]))
->action(__('Go to login'), url(path: '/'));
}
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
//
];
}
}

View File

@ -1,3 +1,23 @@
{ {
"Login": "Вход на сайт" "Login": "Вход на сайт",
"Hello!": "Здравствуйте!",
"Email Address": "Электронная почта",
"Reset Password": "Сбросить пароль",
"Send Password Reset Link": "Восстановить пароль",
"Reset Password Notification": "Восстановление пароля",
"New Password": "Новый пароль",
"Confirm Password": "Введите пароль еще раз",
"You are receiving this email because we received a password reset request for your account.": "Вы получили это письмо после поступившего к нам запроса на сброс пароля для Вашей учетной записи.",
"This password reset link will expire in :count minutes.": "Эта ссылка для восстановления пароля будет действительна :count минут.",
"If you did not request a password reset, no further action is required.": "Если Вы не запрашивали сброс пароля, никаких дальнейших действий не требуется.",
"Regards": "С уважением",
"Your team": "Ваша команда",
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Если кнопка \":actionText\" выше не нажимается, то скопируйте следующую ссылку в адресную строку браузера: ",
"These credentials do not match our records.": "Электронная почта или пароль указаны неверно",
"Account was created": "Ваш личный кабинет Alfa создан",
"Your account was created": "Учетная запись для Вас была создана",
"Please use your email as login and created password": "Пожалуйста, используйте для входа Вашу электронную почту в качестве логина и пароль, который был автоматически сгенерирован: :password",
"Go to login": "Перейти к авторизации",
"Your login": "Ваш логин: :login",
"Your password": "Ваш пароль: :password"
} }

View File

@ -59,32 +59,6 @@ class="form-control @error('email') is-invalid @enderror" name="email"
</div> </div>
</div> </div>
<div class="row mb-3">
<label for="password" class="col-md-4 col-form-label text-md-end">Пароль</label>
<div class="col-md-6">
<input id="password" type="password"
class="form-control @error('password') is-invalid @enderror" name="password"
required autocomplete="new-password">
@error('password')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="row mb-3">
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">Повтор
пароля</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control"
name="password_confirmation" required autocomplete="new-password">
</div>
</div>
<div class="border-top rounded mb-3"> <div class="border-top rounded mb-3">
<div class="row my-3"> <div class="row my-3">
<label for="type" class="col-md-4 col-form-label text-md-end">Юридический <label for="type" class="col-md-4 col-form-label text-md-end">Юридический

View File

@ -0,0 +1,24 @@
@props([
'url',
'color' => 'primary',
'align' => 'center',
])
<table class="action" align="{{ $align }}" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="{{ $align }}">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color }}" target="_blank" rel="noopener">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>

View File

@ -0,0 +1,12 @@
@props(['url'])
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
@if (trim($slot) === 'Alfa')
<img src="{{ url('/images/logo.png') }}" class="logo" alt="Alfa Logo">
@else
{{ $slot }}
@endif
</a>
</td>
</tr>

View File

@ -0,0 +1,57 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>{{ config('app.name') }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{{ $header ?? '' }}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0" style="border: hidden !important;">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}
{{ $subcopy ?? '' }}
</td>
</tr>
</table>
</td>
</tr>
{{ $footer ?? '' }}
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,27 @@
<x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')">
{{ config('app.name') }}
</x-mail::header>
</x-slot:header>
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
<x-slot:subcopy>
<x-mail::subcopy>
{{ $subcopy }}
</x-mail::subcopy>
</x-slot:subcopy>
@endisset
{{-- Footer --}}
<x-slot:footer>
<x-mail::footer>
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
</x-mail::footer>
</x-slot:footer>
</x-mail::layout>

View File

@ -0,0 +1,14 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>

View File

@ -0,0 +1,290 @@
/* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}
body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #718096;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869d4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #3d4852;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #3d4852;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}
/* Logo */
.logo {
height: 75px;
max-height: 75px;
width: 75px;
}
/* Body */
.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
border-bottom: 1px solid #edf2f7;
border-top: 1px solid #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e8e5ef;
border-radius: 2px;
border-width: 1px;
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
margin: 0 auto;
padding: 0;
width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #e8e5ef;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 14px;
}
/* Footer */
.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}
.footer p {
color: #b0adc5;
font-size: 12px;
text-align: center;
}
.footer a {
color: #b0adc5;
text-decoration: underline;
}
/* Tables */
.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}
.table th {
border-bottom: 1px solid #edeff2;
margin: 0;
padding-bottom: 8px;
}
.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}
.content-cell {
max-width: 100vw;
padding: 32px;
}
/* Buttons */
.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
}
.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}
.button-blue,
.button-primary {
background-color: #2d3748;
border-bottom: 8px solid #2d3748;
border-left: 18px solid #2d3748;
border-right: 18px solid #2d3748;
border-top: 8px solid #2d3748;
}
.button-green,
.button-success {
background-color: #48bb78;
border-bottom: 8px solid #48bb78;
border-left: 18px solid #48bb78;
border-right: 18px solid #48bb78;
border-top: 8px solid #48bb78;
}
.button-red,
.button-error {
background-color: #e53e3e;
border-bottom: 8px solid #e53e3e;
border-left: 18px solid #e53e3e;
border-right: 18px solid #e53e3e;
border-top: 8px solid #e53e3e;
}
/* Panels */
.panel {
border-left: #2d3748 solid 4px;
margin: 21px 0;
}
.panel-content {
background-color: #edf2f7;
color: #718096;
padding: 16px;
}
.panel-content p {
color: #718096;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Utilities */
.break-all {
word-break: break-all;
}

View File

@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@ -0,0 +1,9 @@
{!! strip_tags($header ?? '') !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer ?? '') !!}

View File

@ -0,0 +1,27 @@
<x-mail::layout>
{{-- Header --}}
<x-slot:header>
<x-mail::header :url="config('app.url')">
{{ config('app.name') }}
</x-mail::header>
</x-slot:header>
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
<x-slot:subcopy>
<x-mail::subcopy>
{{ $subcopy }}
</x-mail::subcopy>
</x-slot:subcopy>
@endisset
{{-- Footer --}}
<x-slot:footer>
<x-mail::footer>
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
</x-mail::footer>
</x-slot:footer>
</x-mail::layout>

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1,58 @@
<x-mail::message>
{{-- Greeting --}}
@if (! empty($greeting))
# {{ $greeting }}
@else
@if ($level === 'error')
# @lang('Whoops!')
@else
# @lang('Hello!')
@endif
@endif
{{-- Intro Lines --}}
@foreach ($introLines as $line)
{{ $line }}
@endforeach
{{-- Action Button --}}
@isset($actionText)
<?php
$color = match ($level) {
'success', 'error' => $level,
default => 'primary',
};
?>
<x-mail::button :url="$actionUrl" :color="$color">
{{ $actionText }}
</x-mail::button>
@endisset
{{-- Outro Lines --}}
@foreach ($outroLines as $line)
{{ $line }}
@endforeach
{{-- Salutation --}}
@if (! empty($salutation))
{{ $salutation }}
@else
@lang('Regards'),<br>
{{ config('app.name') }}
@endif
{{-- Subcopy --}}
@isset($actionText)
<x-slot:subcopy>
@lang(
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
'into your web browser:',
[
'actionText' => $actionText,
]
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
</x-slot:subcopy>
@endisset
</x-mail::message>