]> _ Git - psq.git/commitdiff
register flow
authorLouis Jeckel <louis.jeckel@outlook.cm>
Thu, 30 Apr 2020 18:55:39 +0000 (20:55 +0200)
committerLouis Jeckel <louis.jeckel@outlook.cm>
Thu, 30 Apr 2020 18:55:39 +0000 (20:55 +0200)
14 files changed:
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/Auth/VerificationController.php
app/Notifications/EmailValidated.php [new file with mode: 0644]
app/Observers/UserObserver.php
app/Providers/AppServiceProvider.php
app/Providers/NovaServiceProvider.php
app/Providers/RouteServiceProvider.php
app/User.php
database/migrations/2020_04_30_093507_add_employer_to_users.php [new file with mode: 0644]
database/migrations/2020_04_30_103918_add_prospect_flag_to_users.php [new file with mode: 0644]
resources/lang/fr/validation.php
resources/views/account/index.blade.php
resources/views/auth/register.blade.php
routes/web.php

index 56a9c2209df6e15888331ecef09d3d7e106efdb8..88a7faf500c9119ac61d7b232ebf18ed6da20ae0 100644 (file)
@@ -5,10 +5,12 @@ namespace App\Http\Controllers\Auth;
 use App\Http\Controllers\Controller;
 use App\Providers\RouteServiceProvider;
 use App\User;
+use \Illuminate\Database\Query\Builder;
 use Illuminate\Foundation\Auth\RegistersUsers;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Hash;
 use Illuminate\Support\Facades\Validator;
+use Illuminate\Validation\Rule;
 
 class RegisterController extends Controller
 {
@@ -51,8 +53,17 @@ class RegisterController extends Controller
     protected function validator(array $data)
     {
         return Validator::make($data, [
-            'name' => ['required', 'string', 'max:255'],
-            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
+            'first_name' => ['required', 'string', 'max:255'],
+            'last_name' => ['required', 'string', 'max:255'],
+            'employer' => ['required', 'string', 'max:255'],
+            'email' => [
+                'required',
+                'string',
+                'email',
+                'max:255',
+                Rule::unique('users')
+                    ->where(fn(Builder $builder) => $builder->where('is_prospect', false))
+            ],
             'password' => ['required', 'string', 'min:8', 'confirmed'],
         ]);
     }
@@ -65,11 +76,27 @@ class RegisterController extends Controller
      */
     protected function create(array $data)
     {
-        return User::create([
-            'name' => $data['name'],
-            'email' => $data['email'],
-            'password' => Hash::make($data['password']),
-        ]);
+        /** @var User $user */
+        $user = User::query()->updateOrCreate(
+            [
+                'email' => $data['email'],
+            ],
+            [
+                'first_name' => $data['first_name'],
+                'last_name' => $data['last_name'],
+                'employer' => $data['employer'],
+                'password' => Hash::make($data['password']),
+                'reg_complete' => true,
+                'is_prospect' => false,
+                'self_registered' => true,
+            ]
+        );
+
+        $user->startTrial();
+
+        $user->sendEmailVerificationNotification();
+
+        return $user;
     }
 
 
index 5e749af86f4351bbba50052c34084f66a30c2f65..cbb8e29f9059335d330124f491e354c7e13e4b38 100644 (file)
@@ -3,8 +3,10 @@
 namespace App\Http\Controllers\Auth;
 
 use App\Http\Controllers\Controller;
+use App\Notifications\EmailValidated;
 use App\Providers\RouteServiceProvider;
 use Illuminate\Foundation\Auth\VerifiesEmails;
+use Illuminate\Http\Request;
 
 class VerificationController extends Controller
 {
@@ -39,4 +41,12 @@ class VerificationController extends Controller
         $this->middleware('signed')->only('verify');
         $this->middleware('throttle:6,1')->only('verify', 'resend');
     }
+
+    /**
+     * @param Request $request
+     */
+    protected function verified(Request $request)
+    {
+        $request->user()->notify(new EmailValidated);
+    }
 }
diff --git a/app/Notifications/EmailValidated.php b/app/Notifications/EmailValidated.php
new file mode 100644 (file)
index 0000000..d6038dd
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Notifications;
+
+use App\User;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Notifications\Notification;
+
+class EmailValidated extends Notification
+{
+    use Queueable;
+
+    /**
+     * Create a new notification instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        //
+    }
+
+    /**
+     * Get the notification's delivery channels.
+     *
+     * @param  mixed  $notifiable
+     * @return array
+     */
+    public function via($notifiable)
+    {
+        return ['mail'];
+    }
+
+    /**
+     * Get the mail representation of the notification.
+     *
+     * @param  mixed  $notifiable
+     * @return \Illuminate\Notifications\Messages\MailMessage
+     */
+    public function toMail($notifiable)
+    {
+        return (new MailMessage)
+                    ->subject('Inscription réussie !')
+                    ->line("Bienvenue ! Vous bénéficiez dès aujourd'hui d'une période d'essai de ".User::trialDurationDays." jours.")
+                    ->line('Merci [....]');
+    }
+
+    /**
+     * Get the array representation of the notification.
+     *
+     * @param  mixed  $notifiable
+     * @return array
+     */
+    public function toArray($notifiable)
+    {
+        return [
+            //
+        ];
+    }
+}
index 0266237853d739eaac6d4779842ad136cfc05cb6..7131edbf236127104604698d0d0bf90ccf5991ca 100644 (file)
@@ -35,9 +35,10 @@ class UserObserver
     public function updating(User $user)
     {
         if(
+            $user->self_registered === false &&
+            $user->reg_complete &&
             $user->isDirty('reg_complete') &&
-            $user->getOriginal()['reg_complete'] === false &&
-            $user->reg_complete
+            $user->getOriginal()['reg_complete'] === false
         ) {
             $user->notify(new RegistrationComplete);
         }
index 3280f56a994cce8121dc37e26ae1e06a40819d1e..93c6fd98acb2eaae8ddc3866d6c477d5909a7cc4 100644 (file)
@@ -7,6 +7,7 @@ use App\Observers\UserObserver;
 use App\PdfFile;
 use App\User;
 use Illuminate\Database\Eloquent\Relations\Relation;
+use Illuminate\Support\Carbon;
 use Illuminate\Support\ServiceProvider;
 use League\HTMLToMarkdown\HtmlConverter;
 use Mailgun\Mailgun;
@@ -49,6 +50,8 @@ class AppServiceProvider extends ServiceProvider
      */
     public function boot()
     {
+        setlocale(LC_TIME, 'fr_FR', 'fr', 'FR', 'French', 'fr_FR.UTF-8');
+        Carbon::setLocale('fr');
         Relation::morphMap([
             'PdfFiles' => PdfFile::class,
         ]);
index 3b3052460076425f04ad610a5724f4d1117a28af..d2697a9803b2883e2304b3ce4d9d055d7612f68d 100644 (file)
@@ -67,7 +67,6 @@ class NovaServiceProvider extends NovaApplicationServiceProvider
             new FileAccess,
             new MailEvents('opened', 'Mails ouverts'),
             new MailEventsPartition,
-
         ];
     }
 
index 540d17b43083574b9ad5185c4982b55f18e31607..3a816d8137d7f06c44a09e30ae48f52068af636b 100644 (file)
@@ -21,7 +21,7 @@ class RouteServiceProvider extends ServiceProvider
      *
      * @var string
      */
-    public const HOME = '/home';
+    public const HOME = '/';
 
     /**
      * Define your route model bindings, pattern filters, etc.
index cc895f7ca8e5d3986c414f62d2c10912834ac34b..3af8e8902d2aa732b6bbcfc43aee84504c8fd7b3 100644 (file)
@@ -3,6 +3,7 @@
 namespace App;
 
 
+use App\Notifications\EmailValidated;
 use DemeterChain\B;
 use Illuminate\Contracts\Auth\MustVerifyEmail;
 use Illuminate\Database\Eloquent\Builder;
@@ -10,6 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Foundation\Auth\User as Authenticatable;
 use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Arr;
 use Illuminate\Support\Carbon;
 use Laravel\Cashier\Billable;
 use Laravel\Scout\Searchable;
@@ -23,8 +25,13 @@ use Laravel\Scout\Searchable;
  * @property-read $name
  * @property Organization $organization
  * @property string $position
- * @property bool $isSubscribed
- * @property Carbon $trial_ends_at
+ * @property bool $isSubscribed (is user has org subscribed)
+ * @property Carbon $trial_ends_at (is user is on trial)
+ * @property bool self_registered (if user used /register)
+ * @property string $employer (used if self registered)
+ * @property bool $is_prospect
+ * @property bool $reg_complete
+ * @property string $status
  */
 class User extends Authenticatable
 {
@@ -71,38 +78,15 @@ class User extends Authenticatable
         'reg_complete' => 'bool',
         'trial_ends_at' => 'datetime',
         'active_until' => 'datetime',
+        'self_registered' => 'bool',
+        'is_prospect' => 'bool',
     ];
 
 
-    public function toSearchableArray()
-    {
-        return [
-            'first_name' => $this->first_name,
-            'last_name' => $this->last_name,
-            'position' => $this->position,
-            'organization' => $this->organization->name ?? null,
-        ];
-    }
-
-    public const trialDurationDays = 14;
-
-    /**
-     * @return BelongsTo
-     */
-    public function organization(): BelongsTo
-    {
-        return $this->belongsTo(Organization::class);
-    }
-
     /**
-     * @return bool
+     * Trial duration in days
      */
-    public function getIsSubscribedAttribute(): bool
-    {
-        return ($o = $this->organization) === null ?
-            false:
-            $o->isSubscribed();
-    }
+    public const trialDurationDays = 14;
 
     /**
      * Possible Statuses
@@ -117,36 +101,50 @@ class User extends Authenticatable
             'label' => 'Abonnement actif (orga)'
         ],
         'trial' => [
-            'badge' => 'warning',
+            'badge' => 'info',
             'label' => "Période d'essai"
         ],
+        'prospect' => [
+            'badge' => 'warning',
+            'label' => 'Prospect',
+        ]
     ];
 
+
+
+
+
+
     /**
-     * @return string
+     * @return array
      */
-    public function getStatusAttribute(): string
+    public function toSearchableArray()
     {
-        $id = 'inactive';
+        return [
+            'first_name' => $this->first_name,
+            'last_name' => $this->last_name,
+            'position' => $this->position,
+            'organization' => $this->organization->name ?? null,
+        ];
+    }
+
 
-        if($this->isSubscribed)
-            $id = 'subscribed';
-        if($this->onTrial())
-            $id = 'trial';
 
-        return $id;
 
-    }
 
     /**
-     * @return string
-     * Returns current status
+     * RELATIONSHIPS
      */
-    public function getStatusLabelAttribute(): string
+
+    /**
+     * @return BelongsTo
+     */
+    public function organization(): BelongsTo
     {
-        return self::statuses[$this->status]['label'];
+        return $this->belongsTo(Organization::class);
     }
 
+
     /**
      * @return HasMany
      */
@@ -155,6 +153,14 @@ class User extends Authenticatable
         return $this->hasMany(LoginToken::class);
     }
 
+
+
+
+
+    /**
+     * METHODS
+     */
+
     /**
      * @param $route
      * @param array $params
@@ -178,32 +184,115 @@ class User extends Authenticatable
         $this->save();
     }
 
+    /**
+     * @param $status
+     * @return bool
+     */
+    public function hasStatus($status): bool
+    {
+        $status = Arr::wrap($status);
+        return in_array($this->status, $status, true);
+    }
 
 
 
 
+    /**
+     * SCOPES
+     */
 
+    /**
+     * @param Builder $builder
+     */
     public function scopeRecievesEmails(Builder $builder): void
     {
         $builder->hasActiveSubscription()->orWhere->isOnTrial();
     }
 
+    /**
+     * @param Builder $builder
+     */
     public function scopeIsOnTrial(Builder $builder): void
     {
         $builder->whereDate('trial_ends_at', '>', now());
     }
 
+    /**
+     * @param Builder $builder
+     */
     public function scopeHasActiveSubscription(Builder $builder): void
     {
         $builder->whereHas('organization', fn($builder) => $builder->subscribed());
     }
 
+    /**
+     * @param Builder $builder
+     */
+    public function scopeProspect(Builder $builder): void
+    {
+        $builder->where('is_prospect', true);
+    }
+
+    /**
+     * @param Builder $builder
+     */
+    public function scopeRegisteredUser(Builder $builder): void
+    {
+        $builder->where('is_prospect', false);
+    }
 
 
 
 
+
+    /**
+     * ATTRIBUTES
+     */
+
+    /**
+     * @return bool
+     * Checks if affiliated organization has valid subscription
+     */
+    public function getIsSubscribedAttribute(): bool
+    {
+        return ($o = $this->organization) === null ?
+            false:
+            $o->isSubscribed();
+    }
+
+
+    /**
+     * @return string
+     * Returns status slug
+     */
+    public function getStatusAttribute(): string
+    {
+        $id = 'inactive';
+
+        if($this->isSubscribed)
+            $id = 'subscribed';
+        if($this->onTrial())
+            $id = 'trial';
+        if($this->is_prospect)
+            $id = 'prospect';
+
+        return $id;
+
+    }
+
+    /**
+     * @return string
+     * Returns current status
+     */
+    public function getStatusLabelAttribute(): string
+    {
+        return self::statuses[$this->status]['label'];
+    }
+
+
     /**
      * @return string|null
+     * Get full name
      */
     public function getNameAttribute(): ?string
     {
@@ -217,6 +306,7 @@ class User extends Authenticatable
 
     /**
      * @return bool
+     * Checks if is affiliated to an organization
      */
     public function getIsIndividualAttribute(): bool
     {
diff --git a/database/migrations/2020_04_30_093507_add_employer_to_users.php b/database/migrations/2020_04_30_093507_add_employer_to_users.php
new file mode 100644 (file)
index 0000000..d81e96c
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddEmployerToUsers extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->string('employer')->nullable();
+            $table->boolean('self_registered')->default(false);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('employer');
+            $table->dropColumn('self_registered');
+        });
+    }
+}
diff --git a/database/migrations/2020_04_30_103918_add_prospect_flag_to_users.php b/database/migrations/2020_04_30_103918_add_prospect_flag_to_users.php
new file mode 100644 (file)
index 0000000..24d9e99
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddProspectFlagToUsers extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->boolean('is_prospect')->default(false);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('is_prospect');
+        });
+    }
+}
index 4cb270529e5b487c9131df57730bc8c4e859ae73..44f9397344e7f4ca6b7391b7aa1bc74e6993f49d 100644 (file)
@@ -46,13 +46,13 @@ return [
     'gt'             => [
         'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
         'file'    => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
-        'string'  => 'Le texte :attribute doit contenir plus de :value caractères.',
+        'string'  => 'Le champ :attribute doit contenir plus de :value caractères.',
         'array'   => 'Le tableau :attribute doit contenir plus de :value éléments.',
     ],
     'gte' => [
         'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
         'file'    => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
-        'string'  => 'Le texte :attribute doit contenir au moins :value caractères.',
+        'string'  => 'Le champ :attribute doit contenir au moins :value caractères.',
         'array'   => 'Le tableau :attribute doit contenir au moins :value éléments.',
     ],
     'image'    => 'Le champ :attribute doit être une image.',
index 28c0fccb34805fd36064ef66d68bca8f148a494e..ca3cfaa9763ad4a15db6925e1f74f171803fa052 100644 (file)
             </div>
             @else
                 @if($user->isSubscribed)
-                    <div class="alert alert-info">
+                    <div class="alert alert-success">
                         Votre compte est actif, vous pouvez accéder aux contenus <b>Prescription Santé.</b>
                     </div>
+
+                @elseif($user->onTrial())
+                <div class="alert alert-info">
+                    Vous bénéficiez d'une période d'évaluation jusqu'au {{$user->trial_ends_at->formatLocalized('%d %B %Y')}}
+                </div>
                 @endif
             @endif
             @if(session()->has('message'))
@@ -50,7 +55,7 @@
                             <label for="last_name" class="col-md-4 col-form-label text-md-right">{{ __('Nom') }}</label>
 
                             <div class="col-md-6">
-                                <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name', $user->last_name) }}" required autocomplete="family-name" autofocus>
+                                <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name', $user->last_name) }}" required autocomplete="family-name">
 
                                 @error('last_name')
                                     <span class="invalid-feedback" role="alert">
index d236a48ecb6d7ffb0d4a4fb62d7be35a79019cac..a3bf374e0256bb19caae6ead93a34605a7e1f328 100644 (file)
@@ -4,7 +4,14 @@
 <div class="container">
     <div class="row justify-content-center">
         <div class="col-md-8">
+
+            <div class="alert-info alert">
+                <p class="font-weight-bold">Bienvenue sur {{config('app.name')}}</p>
+                <p class="mb-0">Vous bénéficierez de 2 semaines d'accès gratuit dès votre inscription !</p>
+            </div>
+
             <div class="card">
+
                 <div class="card-header">{{ __('Register') }}</div>
 
                 <div class="card-body">
                         @csrf
 
                         <div class="form-group row">
-                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
+                            <label for="first_name" class="col-md-4 col-form-label text-md-right">{{ __('Prénom') }}</label>
+
+                            <div class="col-md-6">
+                                <input id="first_name" type="text" class="form-control @error('first_name') is-invalid @enderror" name="first_name" value="{{ old('first_name') }}" required autocomplete="given-name" autofocus>
+
+                                @error('first_name')
+                                    <span class="invalid-feedback" role="alert">
+                                        <strong>{{ $message }}</strong>
+                                    </span>
+                                @enderror
+                            </div>
+                        </div>
+
+                        <div class="form-group row">
+                            <label for="last_name" class="col-md-4 col-form-label text-md-right">{{ __('Nom') }}</label>
 
                             <div class="col-md-6">
-                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
+                                <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name') }}" required autocomplete="family-name">
 
-                                @error('name')
+                                @error('last_name')
                                     <span class="invalid-feedback" role="alert">
                                         <strong>{{ $message }}</strong>
                                     </span>
                             </div>
                         </div>
 
+                        <div class="form-group row">
+                            <label for="employer" class="col-md-4 col-form-label text-md-right">{{ __('Employeur') }}</label>
+
+                            <div class="col-md-6">
+                                <input id="employer" type="text" class="form-control @error('employer') is-invalid @enderror" name="employer" value="{{ old('employer') }}" required>
+
+                                @error('employer')
+                                    <span class="invalid-feedback" role="alert">
+                                        <strong>{{ $message }}</strong>
+                                    </span>
+                                @enderror
+                            </div>
+                        </div>
+
+
+
                         <div class="form-group row">
                             <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
 
index 1d6f1687dfb7c9dd354f9853d9aa04c66ed8cd5a..cc7e6aef5a5e841f3a3de441941ebab627f96897 100644 (file)
@@ -16,7 +16,8 @@ use Illuminate\Support\Facades\Route;
 
 
 
-Auth::routes();
+Auth::routes(['verify' => true]);
+