Peter Muturi 1 yıl önce
ebeveyn
işleme
3a7e2d598e

+ 90 - 0
.env.docker

@@ -0,0 +1,90 @@
+APP_NAME="Let's Be Safe"
+APP_ENV=local
+APP_KEY=base64:n3dXfgGZ7mH1WT54dY7LFyEbOjCf1/hWpvRLysRHozs=
+APP_DEBUG=true
+APP_URL=http://localhost:8001
+
+LOG_CHANNEL=stack
+LOG_DEPRECATIONS_CHANNEL=null
+LOG_LEVEL=debug
+
+DB_CONNECTION=pgsql
+DB_HOST=postgresdb
+DB_PORT=5432
+DB_DATABASE=shopper
+DB_USERNAME=postgres
+DB_PASSWORD=pass
+
+BROADCAST_DRIVER=log
+CACHE_DRIVER=file
+FILESYSTEM_DRIVER=local
+QUEUE_CONNECTION=sync
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+
+MEMCACHED_HOST=127.0.0.1
+
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+
+MAIL_MAILER=smtp
+MAIL_HOST=mailhog
+MAIL_PORT=1025
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_FROM_ADDRESS=hello@villageboarding.com
+MAIL_FROM_NAME="${APP_NAME}"
+
+AWS_ACCESS_KEY_ID=
+AWS_SECRET_ACCESS_KEY=
+AWS_DEFAULT_REGION=us-east-1
+AWS_BUCKET=
+AWS_USE_PATH_STYLE_ENDPOINT=false
+
+PUSHER_APP_ID=
+PUSHER_APP_KEY=
+PUSHER_APP_SECRET=
+PUSHER_APP_CLUSTER=mt1
+
+MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
+BACKEND_URL=http://shopper:8080
+SESSION_KEY_NAME=limeSessionKey
+RECAPTCHA_SITE_KEY=6LdvkjEgAAAAACPPEvT0lOiCF88tLKHOYMhHwBao
+RECAPTCHA_SECRET_KEY=6LdvkjEgAAAAAEtMCrZo0afad_oq_f-80uEFAe-6
+STRIPE_KEY="pk_test_51Kxp8fCz2jQ0Nuhm3RCZyAswValDwfBcmWevW5cKR1EMHCkHHQ5bHOfBraaWCzkqBp8ZZBDkvZENeWAzFtAly2kt00xR6r1lzb"
+STRIPE_SECRET="seti_1KyIsoCz2jQ0NuhmFeA24Pkg_secret_LfeB5Y5MSoti1xGozn0hFNL50hzqe3r"
+
+APP_INTERNAL_NAME=lbs
+PHONE_NUMBER="(800) 750-4540"
+
+GOOGLE_CLIENT_ID=266416419175-agkird52usqthjk4i7k5rcvus0mjn548.apps.googleusercontent.com
+GOOGLE_CLIENT_SECRET=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
+
+FACEBOOK_CLIENT_ID=1210372716515697
+FACEBOOK_CLIENT_SECRET=6535c3111667c60bc73296db2e1fb23b
+
+
+ENABLE_PW_LOGIN=true
+
+check_10_test_panel_with_early_rna=249
+check_10_test_panel=149
+check_hsv_1=19
+check_hsv_2=19
+check_chlamydia=37.5
+check_gonorrhea=37.5
+check_chlamydia_gonorrhea=60
+check_syphillis=39
+check_hepatitis_a=19
+check_hepatitis_b=19
+check_hepatitis_c=19
+check_hiv_antibody=49
+check_hiv_rna=129
+
+OPENTOK_API_KEY=46871644
+OPENTOK_API_SECRET=48c39d640cbcfb1032606d7c40ab5971290a5163
+OPENTOK_SESSION_ID=1_MX40Njg3MTY0NH5-MTU5NjQyMzcxMjQ4OX5PRnNIVmFDU2t2d3BnWG1YbkMvSWFRNk1-fg
+GOOGLE_MAPS_KEY=AIzaSyBTla7vZLk795evCO0Wq2x4qs5Ygwg-iec

+ 14 - 0
.env.example

@@ -72,3 +72,17 @@ INTERCOM_API_SECRET=dG9rOmNkZDFkMWI3X2MwODlfNDAxNV9hYzM5X2Y5YTIwOGU5MjFmYzoxOjA=
 OPENTOK_API_KEY=46871644
 OPENTOK_API_SECRET=48c39d640cbcfb1032606d7c40ab5971290a5163
 OPENTOK_SESSION_ID=1_MX40Njg3MTY0NH5-MTU5NjQyMzcxMjQ4OX5PRnNIVmFDU2t2d3BnWG1YbkMvSWFRNk1-fg
+
+check_10_test_panel_with_early_rna=249
+check_10_test_panel=149
+check_hsv_1=19
+check_hsv_2=19
+check_chlamydia=37.5
+check_gonorrhea=37.5
+check_chlamydia_gonorrhea=60
+check_syphillis=39
+check_hepatitis_a=19
+check_hepatitis_b=19
+check_hepatitis_c=19
+check_hiv_antibody=49
+check_hiv_rna=129

+ 31 - 0
Dockerfile

@@ -0,0 +1,31 @@
+FROM composer:2.4 as build
+
+FROM php:8.1-apache-buster as dev
+
+ENV APP_ENV=dev
+ENV APP_DEBUG=true
+ENV COMPOSER_ALLOW_SUPERUSER=1
+
+RUN apt-get update && apt-get install -y zip
+RUN apt-get install -y libpq-dev
+RUN apt install -y zlib1g-dev libpng-dev && rm -rf /var/lib/apt/lists/*
+RUN docker-php-ext-install pdo pdo_pgsql
+
+COPY . /var/www/html/
+RUN docker-php-ext-install gd
+COPY --from=build /usr/bin/composer /usr/bin/composer
+RUN composer update
+RUN composer install --prefer-dist --no-dev --optimize-autoloader --no-interaction
+
+COPY ./docker/apache/000-default.conf /etc/apache2/sites-available/000-default.conf
+COPY ./.env.docker /var/www/html/.env
+
+RUN php artisan config:clear && \
+    php artisan route:clear && \
+    php artisan cache:clear && \
+    php artisan optimize && \
+    chmod 777 -R /var/www/html/storage/ && \
+    chown -R www-data:www-data /var/www/ && \
+    a2enmod rewrite
+    
+RUN service apache2 restart

+ 15 - 0
app/Helpers.php

@@ -263,4 +263,19 @@ if(!function_exists('friendly_timezone')) {
         }
     }
 }
+if (!function_exists('generate_password')) {
+    function generate_password($length = 20)
+    {
+        $chars =  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' .
+            '0123456789`-=~!@#$%^&*()_+,./<>?;:[]{}\|';
+
+        $str = '';
+        $max = strlen($chars) - 1;
+
+        for ($i = 0; $i < $length; $i++)
+            $str .= $chars[random_int(0, $max)];
+
+        return $str;
+    }
+}
 ?>

+ 83 - 1
app/Http/Controllers/AdminController.php

@@ -190,6 +190,11 @@ class AdminController extends Controller
         return view('app.my-account.admin.orders.sub.dashboard', compact('order'));
     }
 
+    public function orderPreviewInvoice(StoreOrder $order)
+    {
+        return view('app.my-account.admin.orders.sub.preview-invoice', compact('order'));
+    }
+
     public function orderFinancialTransactions(StoreOrder $order)
     {
         $transactions = FinancialTransaction::where('order_id', $order->id)->orderBy('created_at', 'DESC')->get();
@@ -261,7 +266,7 @@ class AdminController extends Controller
                $this->emailService->notifyUserOnFailedTransaction($storeOrder->user);
             }
 
-            return $this->fail($response['message']);
+            return $this->fail($response['message'] ?? 'Failed!');
         }
         return $this->pass();
     }
@@ -300,4 +305,81 @@ class AdminController extends Controller
         }
         return implode("", $output);
     }
+
+    public function orderCreate(Request $request){
+        $userUid = $request->get('userUid');
+        $allTests = config('constants.tests');
+        $selectedTests = $request->get('tests', []);
+        if(!count($selectedTests)){
+            return $this->fail('Please select a test!');
+        }
+        $tests = [];
+        foreach($allTests as $key=>$title){
+            $tests[$key] = in_array($key, $selectedTests) ? 1 : 0;
+        }
+        $data = [
+            'tests' => $tests,
+            'tests_total' => $request->get('total'),
+            'tests_lab_id' => $request->get('tests_lab_id')
+        ];
+
+        $detailJson = [
+            'selected_options' => json_encode($data)
+        ];
+
+        $javaResponse = $this->callJava('/api/customerStore/submitOrderAsAdmin', ['userUid' => $userUid, 'detailJson' => json_encode($detailJson)], $this->sessionKey);
+        if (!@$javaResponse['success']) {
+            return $this->fail($javaResponse['message']);
+        }
+        return $this->pass();
+    }
+
+    public function createNewUser(Request $request){
+        $email = $request->get('email');
+        $existingUser = User::where('email', $email)->first();
+        if ($existingUser) {
+        return $this->fail('This email account is in use!');
+        }
+        $nameFirst = $request->get('fname');
+        $nameLast = $request->get('lname');
+        $name = $nameFirst . ' ' . $nameLast;
+        //call java
+        $temporaryPassword = generate_password(10);
+
+        $userCreateAccountResponse = $this->callJava('/api/auth/signUpWithEmail', [
+        'fullName' => $name,
+        'nameFirst' => $nameFirst,
+        'nameLast' => $nameLast,
+        'email' => $request->get('email'),
+        'phoneNumber' => $request->get('phoneNumber'),
+        'password' => $temporaryPassword,
+        'passwordConfirmation' => $temporaryPassword,
+        ], null);
+
+        if (!@$userCreateAccountResponse['success']) {
+            return $this->fail($userCreateAccountResponse['message']);
+        }
+
+        $newUser = User::where('uid', $userCreateAccountResponse['data'])->first();
+
+        //Send email via java
+        if ($newUser) {
+          $this->emailService->sendUserWelcomeEmail($newUser, $temporaryPassword);
+        }
+        $this->storeUserDetailJson($request, $newUser);
+
+        return $this->pass($userCreateAccountResponse['data']);
+    }
+
+    public function storeUserDetailJson(Request $request, User $user)
+  {
+    $data = [
+      'uid' => $user->uid,
+      'phone_number' => $request->get('phoneNumber'),
+      'notification_option' => $request->get('notificationOption'),
+      'request_change_password' => 1,
+    ];
+
+    $res = $this->callJava('/api/user/upsertDetailJson', $data, $this->sessionKey);
+  }
 }

+ 40 - 0
app/Http/Controllers/MyAccountController.php

@@ -5,6 +5,7 @@ use Illuminate\Http\Request;
 use App\Models\PromoCode;
 use Illuminate\Support\Facades\Cookie;
 use App\Models\PaymentMethod;
+use App\Models\Lab;
 use Laravel\Socialite\Facades\Socialite;
 
 class MyAccountController extends Controller {
@@ -27,4 +28,43 @@ class MyAccountController extends Controller {
         $request->session()->put('user_uid_to_enable_facebook_log_in', $this->user->uid);
         return Socialite::driver('facebook')->redirect();
     }
+
+    public function searchLab(Request $request){
+        $term = $request->input('term') ? trim($request->input('term')) : '';
+        if (empty($term)) return '';
+        $term = strtolower($term);
+
+        $term2 = '';
+        if (strpos($term, ' ') !== FALSE) {
+        $terms = explode(' ', $term);
+        $term = trim($terms[0]);
+        $term2 = trim($terms[1]);
+        }
+        $labs = Lab::query();
+
+
+        $labs = $labs->where(function ($q) use ($term) {
+            $q->orWhereRaw('LOWER(addresss::text) LIKE ?', ['%' . $term . '%']);
+          });
+      
+          if (!empty($term2)) {
+            $labs = $labs->where(function ($q) use ($term2) {
+              $q->orWhereRaw('LOWER(addresss::text) LIKE ?', ['%' . $term2 . '%']);
+            });
+          }
+
+
+        $labs = $labs->get();
+
+        $labsJson = $labs->map(function ($lab) {
+        return [
+            "uid" => $lab->uid,
+            "id" => $lab->id,
+            "text" => $lab->name . ' • ' . $lab->address(true),
+        ];
+        });
+        return json_encode([
+        "results" => $labsJson
+        ]);
+    }
 }

+ 24 - 0
app/Http/Services/EmailService.php

@@ -143,4 +143,28 @@ class EmailService
 
         $response = $this->callJava('/api/email/send', $params, null);
     }
+
+    public function sendUserWelcomeEmail(User $user, $temporaryPassword = null)
+    {
+        if (!$user->getEmail()) return;
+        $appInternalName = $this->appInternalName;
+        $stringMappingConfig = $this->stringMappingConfig;
+        $appUrl = $this->appUrl;
+        $emailFromName = $this->emailFromName;
+        $html = (string) view('emails.templates.user-welcome', compact('user', 'appUrl', 'emailFromName', 'appInternalName', 'stringMappingConfig', 'temporaryPassword'));
+        $plainText = (string) view('emails.templates.user-welcome-txt', compact('user', 'appUrl', 'emailFromName', 'appInternalName', 'stringMappingConfig', 'temporaryPassword'));
+
+        $params = [
+            'fromEmailAddress' => $this->fromEmailAddress,
+            'fromName' => $this->emailFromName,
+            'toEmailAddress' => $user->getEmail(),
+            'subject' => 'Welcome!',
+            'contentHtml' => $html,
+            'contentText' => $plainText,
+            'entityType' => 'USER',
+            'entityUid' => $user->uid,
+        ];
+
+        $response = $this->callJava('/api/email/send', $params, null);
+    }
 }

+ 21 - 0
app/Models/StoreOrder.php

@@ -5,6 +5,7 @@ namespace App\Models;
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 use App\Models\BaseModel;
+use Illuminate\Support\Arr;
 
 class StoreOrder extends BaseModel
 {
@@ -42,6 +43,26 @@ class StoreOrder extends BaseModel
         return (array) @$selectedOptions->tests;
     }
 
+    public function testsRequested(){
+        $tests = $this->tests();
+        $data = [];
+        foreach($tests as $key=>$value){
+            if($value){
+                array_push($data, $key);
+            }
+        }
+        return $data;
+    }
+
+    public function testsRequestedInHumanReadable(){
+        $data = [];
+        $tests = $this->testsRequested();
+        foreach($tests as $test){
+            array_push($data, config('constants.tests.'.$test));
+        }
+        return $data;
+    }
+
 
     public function total(){
         $detail = json_decode($this->detail_json);

+ 14 - 0
config/app.php

@@ -36,6 +36,20 @@ return [
     'opentokApiSecret' => env('OPENTOK_API_SECRET'),
     'opentokSessionId' => env('OPENTOK_SESSION_ID'),
 
+    'check_10_test_panel_with_early_rna' => env('check_10_test_panel_with_early_rna'),
+    'check_10_test_panel' => env('check_10_test_panel'),
+    'check_hsv_1' => env('check_hsv_1'),
+    'check_hsv_2' => env('check_hsv_2'),
+    'check_chlamydia' => env('check_chlamydia'),
+    'check_gonorrhea' => env('check_gonorrhea'),
+    'check_chlamydia_gonorrhea' => env('check_chlamydia_gonorrhea'),
+    'check_syphillis' => env('check_syphillis'),
+    'check_hepatitis_a' => env('check_hepatitis_a'),
+    'check_hepatitis_b' => env('check_hepatitis_b'),
+    'check_hepatitis_c' => env('check_hepatitis_c'),
+    'check_hiv_antibody' => env('check_hiv_antibody'),
+    'check_hiv_rna' => env('check_hiv_rna'),
+
     /*
     |--------------------------------------------------------------------------
     | Application Environment

+ 16 - 0
config/constants.php

@@ -13,6 +13,22 @@ return [
       'clientUrl' => 'https://letsbesafe.com'
     ],
 
+    'tests' => [
+      'check_10_test_panel_with_early_rna' => '10 Test Panel With Early RNA',
+      'check_10_test_panel' => '10 Test Panel',
+      'check_hsv_1' => 'Herpes 1',
+      'check_hsv_2' => 'Herpes 2',
+      'check_chlamydia' => 'Chlamydia',
+      'check_gonorrhea' => 'Gonorrhea',
+      'check_chlamydia_gonorrhea' => 'Chlamydia + Gonorrhea',
+      'check_syphillis' => 'Syphilis',
+      'check_hepatitis_a' => 'Hepatitis A',
+      'check_hepatitis_b' => 'Hepatitis B',
+      'check_hepatitis_c' => 'Hepatitis C',
+      'check_hiv_antibody' => 'HIV-1 + HIV-2',
+      'check_hiv_rna' => 'HIV RNA Early Detection'
+    ],
+
     'countries' => [
         "AF" => "Afghanistan",
         "AL" => "Albania",

+ 14 - 0
docker/apache/000-default.conf

@@ -0,0 +1,14 @@
+<VirtualHost *:80>
+
+  ServerAdmin webmaster@localhost
+  DocumentRoot /var/www/html/public/
+
+  <Directory /var/www/>
+    AllowOverride All
+    Require all granted
+  </Directory>
+
+  ErrorLog ${APACHE_LOG_DIR}/error.log
+  CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+</VirtualHost>

+ 12 - 6
public/css/flyer.css

@@ -1,10 +1,16 @@
-@font-face {
-    font-family: Verdana;
-    src: url("/css/verdana.ttf") format("truetype");
+@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600&display=swapp');
+
+* {
+  font-family: 'Roboto', sans-serif;
+  padding: 0;
+  margin: 0;
+  box-sizing: border-box;
+}
+body {
+  background: #efefef;
+  -webkit-print-color-adjust: exact;
 }
- *:not(i) {
-   font-family: Verdana, sans-serif !important;
- }
+
 @media print {
   .print-none {
     display: none;

+ 0 - 2
public/js/lime.js

@@ -27,7 +27,6 @@ mainApp = null;
                 var placeholder = $(input).attr('placeholder');
                 var url = $(input).data('url');
                 var valueProp = $(input).data('valueprop');
-                var companyId = $(input).data('companyid');
                 var select2 = $(input).select2({
                     placeholder: placeholder,
                     minimumInputLength: 2,
@@ -39,7 +38,6 @@ mainApp = null;
                         data: function (params) {
                             return {
                                 term: params.term,
-                                companyId: companyId,
                                 json: true
                             };
                         },

+ 102 - 0
resources/views/app/my-account/admin/orders/forms/create-order.blade.php

@@ -0,0 +1,102 @@
+<?php
+    $tests = config('constants.tests');
+    $packages = array_filter(
+        $tests,
+        function ($value){
+            return(strpos($value,'Test Panel') !== false);
+        }
+    );
+    $individualTests = array_diff($tests, $packages);
+?>
+<div moe normal>
+    <a start show href="#">+ Create Order</a>
+    <form url="{{ route('admin.create-order') }}" style="min-width: 450px;" right>
+        @csrf
+        <div class="p-3">
+            <h5 class="fw-bold mb-4">Create Order</h5>
+            <div id="createOrder" v-cloak>
+                <input type="hidden" name="total" v-model="form.total" />
+                <input type="hidden" name="userUid" value="{{$user->uid}}" />
+                <div class="form-group mb-3">
+                    <label class="me-3"><input type="radio" name="testType" value="11PanelTest" v-model="form.testType" required /> Comprehensive 11-Panel Test</label>
+                    <label><input type="radio" name="testType" value="individualTests" v-model="form.testType" required /> Individual Tests</label>
+                </div>
+                <div v-if="form.testType === '11PanelTest'" class="px-3">
+                    <div class="d-flex flex-column">
+                        @foreach($packages as $key=>$title)
+                            <label><input type="radio" name="tests[]" value="{{ $key }}" data-amount="{{ config('app.'.$key) }}" required /> {{ $title }} - {{ displayAmount('$', config('app.'.$key)) }}</label>
+                        @endforeach
+                    </div>
+                </div>
+                <div v-if="form.testType === 'individualTests'" class="px-3">
+                    <div class="d-flex flex-column">
+                        @foreach($individualTests as $k=>$t)
+                            <label><input type="checkbox" name="tests[]" value="{{ $k }}" data-amount="{{  config('app.'.$k) }}" /> {{ $t }} - {{ displayAmount('$', config('app.'.$k)) }}</label>
+                        @endforeach
+                    </div>
+                </div>
+                <div v-if="form.total" class="mt-3">
+                    Total: <b>$@{{ form.total }}</b>
+                </div>
+
+                <div class="mb-2">
+                    <label class="mb-1">Lab</label>
+                    <select name="tests_lab_id" auto-suggest-record data-url="/search-lab" data-valueprop="id"  class="form-control">
+                        <option value=""></option>
+                    </select>
+                </div>
+
+            </div>
+            <div class="mt-4">
+                <button submit class="btn btn-sm btn-primary me-2">Create Order</button>
+                <button cancel class="btn btn-sm btn-default border">Cancel</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<script>
+    var createOrderComponent = new Vue({
+        el: '#createOrder',
+        data:{
+            form: {
+              total: 0  
+            },
+            
+        },
+        delimiters: ['@{{', '}}'],
+        methods:{
+            initCalculateTotal: function(){
+                var self = this;
+                $('[name=testType]').change(function(evt){
+                    self.form.total = 0;
+                });
+                $('[data-amount]').change(function(evt){
+                    var total = 0;
+                    var fields = $('[data-amount]');
+                    $.each(fields, function(index, field){
+                        var checked = field.checked ? true:false;
+                        var amount = $(field).data('amount');
+                        if(checked){
+                            total = total + amount;
+                        }
+                    });
+                    self.form.total = total;
+                });
+            },
+            init: function(){
+                
+            }
+        },
+        mounted: function(){
+            this.init();
+        },
+        updated: function(){
+            var self = this;
+            self.$nextTick(function(){
+                self.initCalculateTotal();
+                mainApp.initAutoSuggestRecord();
+            });
+        }
+    });
+</script>

+ 12 - 0
resources/views/app/my-account/admin/orders/partials/order-tests-summary.blade.php

@@ -0,0 +1,12 @@
+<?php
+$tests = (object) $order->testsRequestedInHumanReadable();
+?>
+<table class="table table-sm table-bordered border w-100 mb-0">
+    <tbody>
+        @foreach ($tests as $test)
+            <tr>
+                <td class="w-50 w-lg-75 px-2">{{ $test }}</td>
+            </tr>
+        @endforeach
+    </tbody>
+</table>

+ 6 - 0
resources/views/app/my-account/admin/orders/partials/table.blade.php

@@ -15,6 +15,7 @@
                 <th>#</th>
                 <th>Created At</th>
                 <th>Total Amount</th>
+                <th>Orders</th>
                 <th>Trx</th>
             </tr>
         </thead>
@@ -24,9 +25,14 @@
                     <td class="text-nowrap">
                         <a href="{{ route('admin.orders.view.dashboard', $order) }}"
                             class="me-2">{{ $order->orderNumber() }}</a>
+                            <a href="{{ route('admin.orders.view.preview-invoice', $order) }}"
+                            class="me-2">Invoice</a>
                     </td>
                     <td class="text-nowrap">{{ friendly_date_time($order->created_at) }}</td>
                     <td>{{ displayAmount('$', $order->total()) }}</td>
+                    <td>
+                        @include('app.my-account.admin.orders.partials.order-tests-summary')
+                    </td>
                     <td class="text-nowrap">
                         <?php $finalTrx = $order->finalFinancialTransaction(); ?>
                         @if (@$finalTrx)

+ 25 - 16
resources/views/app/my-account/admin/orders/sub/dashboard.blade.php

@@ -25,19 +25,19 @@
                                 </th>
                             </tr>
 
-                            @if($order->lab())
-                            <tr>
-                                <th class="px-2" colspan="2">
-                                    <span class="text-secondary">Lab:</span>
-                                    <?= $order->lab()->name ?>
-                                </th>
-                            </tr>
-                            <tr>
-                                <th class="px-2" colspan="2">
-                                    <span class="text-secondary">Lab Address:</span>
-                                    <?= $order->lab()->address() ?>
-                                </th>
-                            </tr>
+                            @if ($order->lab())
+                                <tr>
+                                    <th class="px-2" colspan="2">
+                                        <span class="text-secondary">Lab:</span>
+                                        <?= $order->lab()->name ?>
+                                    </th>
+                                </tr>
+                                <tr>
+                                    <th class="px-2" colspan="2">
+                                        <span class="text-secondary">Lab Address:</span>
+                                        <?= $order->lab()->address() ?>
+                                    </th>
+                                </tr>
                             @endif
                             <tr>
                                 <th class="px-2" colspan="2">
@@ -54,16 +54,25 @@
             <div class="card">
                 <div class="card-header">
                     <div class="d-flex align-items-center justify-content-between">
-                        <h5 class="mb-0 fw-bold">Tests</h5>
+                        <h5 class="mb-0 fw-bold">Tests Requested</h5>
                     </div>
                 </div>
                 <div class="card-body">
                     <div>
                         <?php
-                        $tests = $order->tests();
-                        parseRender($tests);
+                        $tests = (object) $order->testsRequestedInHumanReadable();
                         ?>
+
                     </div>
+                    <table class="table table-sm table-bordered border w-100 mb-0">
+                        <tbody>
+                            @foreach ($tests as $test)
+                                <tr>
+                                    <td class="w-50 w-lg-75 px-2">{{ $test }}</td>
+                                </tr>
+                            @endforeach
+                        </tbody>
+                    </table>
                 </div>
             </div>
         </div>

+ 175 - 1
resources/views/app/my-account/admin/orders/sub/preview-invoice.blade.php

@@ -1 +1,175 @@
-@include('app.my-account.admin.users.partials.invoice', ['storeOrder' => $order, 'appUrl' =>config('app.url'), 'appConfig' => $stringMappingConfig])
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+
+<head>
+    <?php
+      $appInternalName = config('app.internalName');
+      $appConfig = config('constants.' . $appInternalName);
+     ?>
+    <meta charset="utf-8">
+    <title>{{$appConfig['name']}}</title>
+    <link rel="icon" href="{{asset($appConfig['favicon'])}}">
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
+    <link rel="stylesheet" href="{{asset('vendor/fontawesome/css/all.min.css')}}">
+    <link rel="stylesheet" href="{{asset('css/style.css')}}">
+    <link rel="stylesheet" href="{{asset('css/flyer.css')}}">
+    <style>
+        :root {
+        --pry-color:{{$appConfig['primaryColor']}};
+      }
+    </style>
+</head>
+
+<body>
+    <div class="print-none bg-white py-3 mb-4">
+        <div class="container p-0" style="width:8.5in">
+            <div class="row justify-content-between align-items-center">
+                <div class="col-5 text-start">
+                    <a href="{{url()->previous()}}"><i class="fal fa-angle-left me-1"></i> Go back</a>
+                </div>
+                <div class="col-5">
+                    <a href="javascript:;" onclick="return printPDF()" class="btn btn-dark py-3 w-100 rounded-0 shadow"><i class="fal fa-print me-1"></i> Print</a>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="coupon-container px-5 py-4">
+      <div class="d-flex align-items-center justify-content-between my-4">
+        <span class="navbar-brand"><img src="{{asset($appConfig['logo'])}}" height="60" alt=""></span>
+        <h4 class="md-title text-pry m-0">INVOICE</h4>
+      </div>
+      <div class="mb-3 d-flex align-items-start justify-content-between">
+        <div class="">
+          <p class="f-fallback" style="color: #000; font-size: 16px;"><b>The {{$appConfig['name']}} Team</b>
+          <span style="display:block;" class="mt-3">Basic Nutrition, Inc.</span>
+
+          11140 Rockville Pike<br>
+          Rockville, MD 20852
+          </p>
+        </div>
+        <div class="text-end">
+          <p class="mb-3" style="color: #000; font-size: 16px;"><b>Contact Us</b></p>
+          <p style="font-size:15px">{{$appConfig['supportEmail']}}</p>
+          <p style="font-size:15px">{{$appConfig['supportPhone']}}</p>
+          <p style="font-size:15px">{{$appConfig['productUrl']}}</p>
+        </div>
+      </div>
+        <table border="0" cellspacing="0" cellpadding="0" style="background:#fff; border-collapse:collapse;border-spacing:0;margin-bottom:30px;margin-top:10px;margin-inline:auto;">
+            <tbody>
+                <tr>
+                    <td>
+                        <table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0;width:100%;color:rgb(51,51,51);font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">
+                            <tbody>
+                                <tr height="24" style="background-color:rgb(250,250,250)">
+                                    <td colspan="5" valign="top" style="text-align:center;text-transform:uppercase;padding:10px;border-top-left-radius:3px;border-bottom-left-radius:3px">
+                                        <span style="font-size:15px;font-weight:600;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">Order Information</span>
+                                    </td>
+                                </tr>
+                                <tr height="15">
+                                    <td colspan="5"></td>
+                                </tr>
+                                <tr>
+                                    <td colspan="3" style="width:200px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">Date:
+                                        <?= date('F d, Y', strtotime($order->created_at)) ?>
+                                    </td>
+                                    <td colspan="2" align="right" valign="top" style="width:430px;padding:0 6px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">Order: #{{$order->orderNumber()}}</td>
+                                </tr>
+                                <tr height="15">
+                                    <td colspan="5"></td>
+                                </tr>
+                                <tr height="24" style="background-color:rgb(250,250,250)">
+                                    <td colspan="5" valign="top" style="text-align:center;text-transform:uppercase;padding:6px;border-top-left-radius:3px;border-bottom-left-radius:3px"><span style="font-size:15px;font-weight:600">Tests Ordered</span></td>
+                                </tr>
+                                <tr height="15">
+                                    <td colspan="5"></td>
+                                </tr>
+                                @foreach($order->testsRequested() as  $tkey => $test)
+                                <tr>
+                                  <td colspan="3" style="padding:0 0 0 15px;vertical-align:top;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">
+                                    <span style="font-weight:600;">{{ str_replace('Check ', '', toHumanReadable($test)) }}</span>
+                                  </td>
+                                  <td align="right" valign="top" style="padding:0 5px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">
+                                    <span style="font-weight:600;white-space:nowrap;display:block;">${{number_format(config('app.'.$test), 2)}}</span>
+                                  </td>
+                                </tr>
+                                @endforeach
+                            </tbody>
+                        </table>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0;width:100%;color:rgb(51,51,51);font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">
+                            <tbody>
+                                <tr height="10">
+                                    <td colspan="3"></td>
+                                </tr>
+                                <tr height="10">
+                                    <td height="1" colspan="3">
+                                        <div style="line-height:1px;height:1px;background-color:rgb(238,238,238)"></div>
+                                    </td>
+                                </tr>
+                                <tr height="28">
+                                    <td align="right" style="color:#000;font-size:14px;font-weight:600;padding:0 30px 0 0;border:none;border-width:1px;border-color:rgb(238,238,238)">TOTAL</td>
+                                    <td width="1" style="background-color:rgb(238,238,238);width:1px"></td>
+                                    <td width="90" align="right" style="width:120px;font-size:16px;font-weight:600;white-space:nowrap">
+                                        ${{number_format($order->total(), 2)}}
+                                    </td>
+                                </tr>
+                                <tr height="10">
+                                    <td height="1" colspan="3">
+                                        <div style="line-height:1px;height:1px;background-color:rgb(238,238,238)"></div>
+                                    </td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </td>
+                </tr>
+                <tr height="15">
+                    <td colspan="3"></td>
+                </tr>
+                <tr>
+                  <td>
+                    <table border="0" cellpadding="0" cellspacing="0" style="border-collapse:collapse;border-spacing:0;color:rgb(51,51,51);background-color:rgb(250,250,250);border-radius:3px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif">
+                      <tbody>
+                        <tr height="66">
+                          <td width="620" rowspan="3" style="vertical-align:initial;padding:20px;border-style:solid;border-color:white;border-left-width:0px;border-right-width:0px;border-top-width:0px;border-bottom-width:0px">
+                            <span style="color:#000;font-size:15px;padding:5px 0;text-transform:uppercase;font-weight:600;">Lab Information</span><br> <br>
+                            <span style="font-weight:bold;">{{@$order->lab()->locatorlocationname}}</span> <br>
+                            {!! @$order->lab() ? @$order->lab()->address() : '' !!}<br>
+                            <br>
+                          </td>
+                          <td width="595" rowspan="3" style="vertical-align:initial;padding:20px;border-style:solid;border-color:white;border-left-width:0px;border-right-width:0px;border-top-width:0px;border-bottom-width:0px">
+                            <span style="color:#000;font-size:15px;padding:5px 0;text-transform:uppercase;font-weight:600;">Billing Information</span><br> <br>
+                            <span style="font-weight:bold;">Bill To:</span> <br>
+                            {{$order->client->displayName()}}<br>
+                            <br>
+                            @if($order->paymentMethod)
+                            <span style="font-weight:bold;">Payment Method: </span> <br>
+                            <span style="margin-right:5px;text-transform:uppercase;">{{$order->paymentMethod->brand()}}</span> {{$order->paymentMethod->displayNameShort()}}
+                            @endif
+                          </td>
+                        </tr>
+                      </tbody>
+                    </table>
+                  </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+    <div class="print-none mt-4">
+        <footer class="bg-dark text-md-center py-2 d-flex justify-content-center flex-wrap">
+            <div class="text-white mt-lg-0 mt-2">
+                <small class="opacity-75">&copy; Copyright {{date('Y')}} Basic Nutrition, Inc. All rights reserved. <br class="d-lg-block d-none"> <span class="trademark trademark-inline">{{$appConfig['name']}}</span> is a registered trademark of
+                    Basic Nutrition, Inc.</small>
+            </div>
+        </footer>
+    </div>
+</body>
+<script type="text/javascript">
+    function printPDF() {
+        window.print();
+    }
+</script>
+
+</html>

+ 37 - 32
resources/views/app/my-account/admin/users/forms/create.blade.php

@@ -1,41 +1,46 @@
 <div moe relative>
     <a start show href="#">+ New User</a>
-    <form url="/api/affiliate/createCustomerAsReseller">
+    <form url="{{ route('admin.create-new-user') }}" redir="/admin/users/view/[data]/orders">
         <h4 class="fw-bold mb-4">New User</h4>
-        <div class="mb-3">
-            <label class="text-sm text-secondary mb-1">Coupon</label>
-            <?php $coupons = \App\Models\PromoCode::orderBy('code', 'ASC')->get(); ?>
-            <select name="promoCodeUid" class="form-control form-control-sm" required>
-                <option value="">-- select --</option>
-                @foreach($coupons as $coupon)
-                    <option value="{{$coupon->uid}}">{{$coupon->code}}</option>
-                @endforeach
-            </select>
-        </div>
-        <div class="mb-2">
-            <label class="text-sm text-secondary mb-1">First Name</label>
-            <input type="text" name="nameFirst" class="form-control form-control-sm" required onkeyup="generateFullName(this)">
-        </div>
-        <div class="mb-2">
-            <label class="text-sm text-secondary mb-1">Last Name</label>
-            <input type="text" name="nameLast" class="form-control form-control-sm" required onkeyup="generateFullName(this)">
-        </div>
-        <input type="hidden" name="fullName">
-        <div class="mb-2">
-            <label class="text-sm text-secondary mb-1">Email Address</label>
-            <input type="email" name="email" class="form-control form-control-sm" required>
-        </div>
-        <div class="mb-2">
-            <label class="text-sm text-secondary mb-1">Temporary Password</label>
-            <input type="password" name="temporaryPassword" class="form-control form-control-sm" required>
-        </div>
-        <div class="mb-3">
-            <label class="text-sm text-secondary mb-1">Welcome Message</label>
-            <textarea name="welcomeMessage" class="form-control form-control-sm" rows="2"></textarea>
+        <div id="createNewUser" v-cloak>
+            <div class="mb-2">
+                <label class="text-sm text-secondary mb-1">First Name*</label>
+                <input type="text" name="fname" class="form-control form-control-sm" required />
+            </div>
+            <div class="mb-2">
+                <label class="text-sm text-secondary mb-1">Last Name*</label>
+                <input type="text" name="lname" class="form-control form-control-sm" required />
+            </div>
+            <div class="mb-2">
+                <label class="text-sm text-secondary mb-1">Email Address*</label>
+                <input type="email" name="email" class="form-control form-control-sm" required>
+            </div>
+            <div class="mb-2">
+                <label class="text-sm text-secondary mb-1">How would you like us to notify you when your results are available?*</label>
+                <select class="form-control form-control-sm" name="notificationOption" v-model="form.notificationOption">
+                    <option value="">Select</option>
+                    <option value="email">Email Me</option>
+                    <option value="phone">Text Me (SMS)</option>
+                    <option value="none">Do not contact me, I will call in to check on my results.</option>
+                </select>
+            </div>
+            <div v-if="form.notificationOption === 'phone'" class="mb-2">
+                <label class="text-sm text-secondary mb-1">Phone Number*</label>
+                <input type="text" name="phoneNumber" class="form-control form-control-sm" required />
+            </div>
         </div>
         <div class="d-flex align-items-center mt-3">
             <button type="button" class="btn py-2 btn-grey border w-100 no-shadow" cancel>Cancel</button>
             <button class="btn py-2 btn-primary w-100 ms-3" submit>Submit</button>
         </div>
     </form>
-</div>
+</div>
+
+<script>
+    var createNewUserComponent = new Vue({
+        el:'#createNewUser',
+        data:{
+            form:{}
+        }
+    });
+</script>

+ 1 - 1
resources/views/app/my-account/admin/users/index.blade.php

@@ -3,7 +3,7 @@
     <div class="px-3">
         <div class="d-flex align-items-baseline border-bottom px-3 py-2 mb-2 m-neg-3 bg-light">
             <h2 class="font-size-16 text-secondary fw-bold m-0 me-3">Users</h2>
-            {{--@include('app.my-account.admin.users.forms.create')--}}
+            @include('app.my-account.admin.users.forms.create')
         </div>
         @include('app.my-account.admin.users.filters')
         @include('app.my-account.admin.users.partials.table', ['rows' => $users])

+ 2 - 0
resources/views/app/my-account/admin/users/sub/info/name.blade.php

@@ -21,6 +21,8 @@
             @if($user->is_agreed_as_rep || $user->is_agreed_as_manager)
                 <span class="me-2">PayPal Email: <b>{{ $user->paypal_email_address ?? '--' }}</b></span>
             @endif
+            <span class="me-2">Phone Number: <b>{{ $user->phone_number ?? $user->getDetailJsonValue('phone_number') }}</b></span>
+            <span class="me-2">Notification Option: <b>{{ $user->phone_number ?? $user->getDetailJsonValue('notification_option') }}</b></span>
         </div>
     </div>
     <div>

+ 16 - 1
resources/views/app/my-account/admin/users/sub/orders.blade.php

@@ -2,7 +2,13 @@
 
 @section('details')
     <div class="my-4">
-        <h4><b>Orders as Client</b></h4>
+        <div class="d-flex align-items-center justify-content-between">
+            <h4 class="m-0"><b>Orders as Client</b></h4>  
+            <div>
+                @include('app.my-account.admin.orders.forms.create-order')
+            </div>
+        </div>
+        
         <div class="table-responsive">
             <table class="table table-sm table-hover table-striped table-bordered mb-0">
                 <thead>
@@ -10,6 +16,7 @@
                         <th>IID</th>
                         <th>Created At</th>
                         <th>Total Amount</th>
+                        <th>Tests</th>
                         <th>Trx</th>
                     </tr>
                 </thead>
@@ -20,6 +27,9 @@
                             </td>
                             <td>{{ friendly_date($order->created_at) }}</td>
                             <td>{{ displayAmount('$', $order->total()) }}</td>
+                            <td>
+                                @include('app.my-account.admin.orders.partials.order-tests-summary')
+                            </td>
                             <td>
                                 <div>
                                     @include('app.my-account.admin.orders.forms.create-charge')
@@ -55,6 +65,11 @@
                             </td>
                         </tr>
                     @endforeach
+                    @if(!count($orders))
+                        <tr>
+                            <td colspan="5">No orders placed!</td>
+                        </tr>
+                    @endif
                 </tbody>
             </table>
         </div>

+ 20 - 0
resources/views/emails/templates/user-welcome-txt.blade.php

@@ -0,0 +1,20 @@
+Welcome, {{$user->name_display}}!
+
+Thank your for signing up.
+
+Please visit {{$appUrl}} to access your account.
+
+@if($temporaryPassword)
+Your temporary password is: {{ $temporaryPassword }}
+@endif
+
+If you have any questions, feel free to email our customer success team
+(We're lightning quick at replying.) We also offer live chat during business hours.
+
+Cheers,
+{{$emailFromName}}
+
+-----------
+
+
+©{{date('Y')}} {{ $stringMappingConfig['name'] }} -> {{$appUrl}}. All rights reserved.

+ 18 - 0
resources/views/emails/templates/user-welcome.blade.php

@@ -0,0 +1,18 @@
+@extends('emails.layout')
+@section('salutation')
+    Welcome, {{$user->displayName()}}!
+@endsection
+
+@section('content')
+    @if($temporaryPassword)
+        <p class="f-fallback" style="color: #000; font-size: 17px; padding: 0 15px; line-height: 30px; margin: .4em 0 0.1875em;">Thank you for signing up. Below is your temporary password to login.<br />
+        <span><b>{{ $temporaryPassword }}</b></span>
+    </p>
+    @else
+        <p class="f-fallback" style="color: #000; font-size: 17px; padding: 0 15px; line-height: 30px; margin: .4em 0 0.1875em;">Thank you for signing up. We’re thrilled to have you onboard. Please click the link below to complete the process.</p>
+    @endif
+    
+
+    @include('emails.call-to-action-button', ['link'=>$stringMappingConfig['clientUrl'] .'/my-account', 'label' => 'Access your account'])
+
+@endsection

+ 4 - 0
routes/web.php

@@ -35,6 +35,7 @@ Route::middleware('ensureUserNotLoggedIn')->group(function () {
 });
 
 Route::group(['middleware' => ['ensureUserLoggedIn']], function () {
+    Route::get('/search-lab', [MyAccountController::class, 'searchLab'])->name('search-lab');
     Route::prefix('/my-account')->name('my-account')->group(function () {
         Route::get('/', [MyAccountController::class, 'index'])->name('.index');
         Route::get('/log-in-settings', [MyAccountController::class, 'logInSettings'])->name('.log-in-settings');
@@ -48,6 +49,7 @@ Route::group(['middleware' => ['ensureUserLoggedIn']], function () {
         Route::prefix('/admin')->name('admin')->group(function () {
             Route::get('/dashboard', [AdminController::class, 'dashboard'])->name('.dashboard');
             Route::get('/users', [AdminController::class, 'users'])->name('.users');
+            Route::post('/create-new-user', [AdminController::class, 'createNewUser'])->name('.create-new-user');
             Route::prefix('/users/view/{user}')->name('.users.view')->group(function () {
                 Route::get('/dashboard', [AdminController::class, 'userDashboard'])->name('.dashboard');
                 Route::get('/orders', [AdminController::class, 'userOrders'])->name('.orders');
@@ -60,8 +62,10 @@ Route::group(['middleware' => ['ensureUserLoggedIn']], function () {
             });
             
             Route::get('/orders', [AdminController::class, 'orders'])->name('.orders');
+            Route::post('/create-order', [AdminController::class, 'orderCreate'])->name('.create-order');
             Route::prefix('/orders/view/{order}')->name('.orders.view')->group(function () {
                 Route::get('/dashboard', [AdminController::class, 'orderDashboard'])->name('.dashboard');
+                Route::get('/preview-invoice', [AdminController::class, 'orderPreviewInvoice'])->name('.preview-invoice');
                 Route::get('/financial-transactions', [AdminController::class, 'orderFinancialTransactions'])->name('.financial-transactions');
                 Route::post('/order-charge', [AdminController::class, 'orderCharge'])->name('.order-charge');
             });

+ 0 - 0
storage/app/.gitignore