bug fixes, mno,client contract renewals, scheduling

This commit is contained in:
Kwesi Banson
2024-06-10 21:40:28 +00:00
parent 1a4a3acfda
commit 464b544587
110 changed files with 531 additions and 127 deletions

BIN
.DS_Store vendored Executable file

Binary file not shown.

BIN
app/.DS_Store vendored Executable file

Binary file not shown.

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Http\Controllers\ClientContractRenewalAlertsController;
class ProcessClientContractRenewalAlert extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'client_renewal:send';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send notices on client pending contract renewals';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(ClientContractRenewalAlertsController $client_contractRenewalReminder)
{
parent::__construct();
$this->client_contractRenewalReminder = $client_contractRenewalReminder;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->client_contractRenewalReminder->getClientDetails();
\Log::info('SendClientContractRenewalReminders command completed...');
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Http\Controllers\ContractRenewalReminderController;
class SendContractRenewalReminders extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'renewal:send';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send notices on pending contract renewals';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(ContractRenewalReminderController $contractRenewalReminder)
{
parent::__construct();
$this->contractRenewalReminder = $contractRenewalReminder;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->contractRenewalReminder->getMnos();
\Log::info('SendContractRenewalReminders command completed...');
}
}

View File

@@ -13,7 +13,8 @@ class Kernel extends ConsoleKernel
* @var array * @var array
*/ */
protected $commands = [ protected $commands = [
// Commands\SendContractRenewalReminders::class
Commands\ProcessClientContractRenewalAlert::class
]; ];
/** /**
@@ -26,6 +27,8 @@ class Kernel extends ConsoleKernel
{ {
// $schedule->command('inspire') // $schedule->command('inspire')
// ->hourly(); // ->hourly();
$schedule->command('renewal:send')->weekdays()->at('13:00');
$schedule->command('client_renewal:send')->weekdays()->at('14:00');
} }
/** /**

BIN
app/Http/.DS_Store vendored Executable file

Binary file not shown.

BIN
app/Http/Controllers/.DS_Store vendored Executable file

Binary file not shown.

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Session;
use Illuminate\Support\Arr;
use App\Jobs\SendClientContractRenewalAlert;
use App\Http\Requests;
use Carbon\Carbon;
class ClientContractRenewalAlertsController extends Controller
{
public function getClientDetails(){
$client_arr = Models\Client::with('auth_user_info')->where('contract_auto_renew', 'NO')->get();
// dd($client_arr);
$renew_ready = [];
$current_date = date('Y-m-d');
foreach ($client_arr as $value) {
if ($value->contract_validity == false) {
continue;
}
$date1 = date_create($current_date);
$date2 = date_create($value->contract_validity);
$difference = date_diff($date1, $date2);
if ($difference->days <= 45) {
$renew_ready['client_name'] = $value->name;
$renew_ready['account_manager_email'] = $value->auth_user_info->email;
$renew_ready['days_to_expire'] = $difference->days;
dispatch(new SendClientContractRenewalAlert($renew_ready));
}
}
//dump($renew_ready);
$log_data = implode(', ', $renew_ready);
\Log::info('Clients due for renewal ' . $log_data);
//$this->sendNtfy('Clients due for renewal ' . $log_data);
}
}

View File

@@ -776,6 +776,7 @@ class ClientsController extends Controller
'codes_data' => $codes_data, 'codes_data' => $codes_data,
'type' => $type 'type' => $type
]; ];
// dd($data);
return view('client.shortcodes', $data); return view('client.shortcodes', $data);
} }
/** /**

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models;
use Session;
use Illuminate\Support\Arr;
use App\Jobs\SendMnoContractRenewalEmailAlert;
use App\Http\Requests;
use Carbon\Carbon;
class ContractRenewalReminderController extends Controller
{
public function getMnos(){
$network_arr = Models\NetworkOps::with('account_manager_info')->where('contract_auto_renew', 'NO')->get();
$renew_ready = [];
$current_date = date('Y-m-d');
foreach ($network_arr as $value) {
if ($value->contract_validity == false) {
continue;
}
$date1 = date_create($current_date);
$date2 = date_create($value->contract_validity);
$difference = date_diff($date1, $date2);
if ($difference->days <= 45) {
$renew_ready['mno_name'] = $value->name;
$renew_ready['account_manager_email'] = $value->account_manager_info->email;
$renew_ready['days_to_expire'] = $difference->days;
dispatch(new SendMnoContractRenewalEmailAlert($renew_ready));
}
}
$log_data = implode(', ', $renew_ready);
\Log::info('MNOs due for renewal ' . $log_data);
$this->sendNtfy('MNOs due for renewal ' . $log_data);
}
}

View File

@@ -28,12 +28,14 @@ class Controller extends BaseController
} }
public function sendNtfy($data){ public function sendNtfy($data){
$url = 'https://ntfy.sh/mMjh6hStlSQiyiUr';
$url_b = 'https://ntfy.sh/SansaTest';
$curl = curl_init(); $curl = curl_init();
curl_setopt_array($curl, array( curl_setopt_array($curl, array(
CURLOPT_HTTPHEADER => array( CURLOPT_HTTPHEADER => array(
'Content-Type: application/json' 'Content-Type: application/json'
), ),
CURLOPT_URL => 'https://ntfy.sh/SansaTest', CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '', CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10, CURLOPT_MAXREDIRS => 10,

View File

@@ -9,7 +9,7 @@ use Session;
class DashboardController extends Controller class DashboardController extends Controller
{ {
public function index(){ public function index(){
// dd('foo bar');
$total_clients = Models\Client::count(); $total_clients = Models\Client::count();
$ussd_clients = Models\Client::where('services', 'LIKE', '%ussd%')->orwhere('services', 'LIKE', '%A2P%')->count(); $ussd_clients = Models\Client::where('services', 'LIKE', '%ussd%')->orwhere('services', 'LIKE', '%A2P%')->count();
$sms_clients = Models\Client::where('services', 'LIKE', '%sms%')->count(); $sms_clients = Models\Client::where('services', 'LIKE', '%sms%')->count();

View File

@@ -33,6 +33,7 @@ class GeneralDocumentsController extends Controller
'rates' => 'SMS/Voice Rates', 'rates' => 'SMS/Voice Rates',
'scfees' => 'Short Code Fees', 'scfees' => 'Short Code Fees',
'vpn_forms' => 'VPN Forms', 'vpn_forms' => 'VPN Forms',
'user_guides' => 'User Guides',
'sidwl' => 'SID Whitelisting Letter', 'sidwl' => 'SID Whitelisting Letter',
'others' => 'Others' 'others' => 'Others'
]; ];
@@ -109,6 +110,7 @@ class GeneralDocumentsController extends Controller
'scfees' => 'Short Code Fees', 'scfees' => 'Short Code Fees',
'presentations' => 'presentations', 'presentations' => 'presentations',
'vpn_forms' => 'VPN Forms', 'vpn_forms' => 'VPN Forms',
'user_guides' => 'User Guides',
'others' => 'Others' 'others' => 'Others'
]; ];

View File

@@ -17,7 +17,7 @@ class LoginController extends Controller
public function handleLogin(Request $request){ public function handleLogin(Request $request){
$this->validate($request, ['email' => 'required', 'password' => 'required']); $this->validate($request, ['email' => 'required', 'password' => 'required']);
//system user : $table = "auth_users";
$logged_in = Models\SystemUser::with('designation_info')->where('email', $request->email)->where('password', md5($request->password))->first(); $logged_in = Models\SystemUser::with('designation_info')->where('email', $request->email)->where('password', md5($request->password))->first();
if(empty($logged_in)){ if(empty($logged_in)){
@@ -39,6 +39,9 @@ class LoginController extends Controller
$this->storeLoggedUser(); $this->storeLoggedUser();
switch ($logged_in->designation_info->name) { switch ($logged_in->designation_info->name) {
case 'Administrator':
return redirect(url('finance')); // change it to a combined dashboard
break;
case 'Accounts & Finance': case 'Accounts & Finance':
return redirect(url('finance')); return redirect(url('finance'));
break; break;

View File

@@ -63,6 +63,7 @@ class NetworkOperatorsController extends Controller
'page_title' => 'Mobile Network Operators', 'page_title' => 'Mobile Network Operators',
'current_user' => session('current_user') 'current_user' => session('current_user')
]; ];
// dd($data);
return view('network_ops.index', $data); return view('network_ops.index', $data);
} }
public function getMnosJson(Request $request) public function getMnosJson(Request $request)

View File

@@ -32,6 +32,9 @@ class UtilityController extends Controller
return view('utility.map'); return view('utility.map');
} }
public function ntfyTest(){
$this->sendNtfy('In the ERP');
}
function insertOnboardingProgress(){ function insertOnboardingProgress(){
$all_clients = Models\Client::get(); $all_clients = Models\Client::get();
$count_cl = 0; $count_cl = 0;

View File

@@ -38,6 +38,8 @@ class UpdateMnoRequest extends FormRequest
'contact_person' => 'required', 'contact_person' => 'required',
'contact_person_email' => 'required', 'contact_person_email' => 'required',
'contact_person_phone' => 'required', 'contact_person_phone' => 'required',
'contract_auto_renew' => 'required',
'contract_validity' => 'required',
'document_one' => 'max:20480|mimes:png,jpg,jpeg,bmp,pdf,doc,docx,xlx,xlsx', 'document_one' => 'max:20480|mimes:png,jpg,jpeg,bmp,pdf,doc,docx,xlx,xlsx',
'document_two' => 'max:20480|mimes:png,jpg,jpeg,bmp,pdf,doc,docx,xlx,xlsx', 'document_two' => 'max:20480|mimes:png,jpg,jpeg,bmp,pdf,doc,docx,xlx,xlsx',
]; ];

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Jobs;
use App\Models;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Mail\Mailer;
class SendClientContractRenewalAlert implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $renewalSet;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($renewalSet)
{
$this->renewalSet = $renewalSet;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(Mailer $mailer){
$renewalSet = $this->renewalSet;
$emails = ['samuel@click-mobile.com'];
$emails = ['kwesi@click-mobile.com'];
$emails = ['effie@click-mobile.com'];
$emails[] = $renewalSet['account_manager_email'];
$data = [
'client_name' => $renewalSet['client_name'],
'alert_body' => 'Contract for ' . $renewalSet['client_name'] . ' will expire in ' . $renewalSet['days_to_expire'] . ' days. Take note'
];
$mailer->send('emails.client_renewal-alert', $data, function ($message) use ($data, $emails) {
$message->from('support@click-mobile.com', 'Click Mobile ERP');
$message->to($emails)->subject('Client Contract Renewal Alert');
});
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Jobs;
use App\Models;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Contracts\Mail\Mailer;
class SendMnoContractRenewalEmailAlert implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $renewalSet;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($renewalSet)
{
$this->renewalSet = $renewalSet;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(Mailer $mailer){
$renewalSet = $this->renewalSet;
$emails = ['samuel@click-mobile.com'];
$emails = ['kwesi@click-mobile.com'];
$emails = ['effie@click-mobile.com'];
$emails[] = $renewalSet['account_manager_email'];
$data = [
'mno_name' => $renewalSet['mno_name'],
'alert_body' => 'Contract for ' . $renewalSet['mno_name'] . ' will expire in ' . $renewalSet['days_to_expire'] . ' days. Take note'
];
\Log::info("Contract Renewal Alert triggered by : ");
$mailer->send('emails.renewal-alert', $data, function ($message) use ($data, $emails) {
$message->from('support@click-mobile.com', 'Click Mobile ERP');
$message->to($emails)->subject('Contract Renewal Alert');
});
}
}

BIN
app/Models/.DS_Store vendored Executable file

Binary file not shown.

View File

@@ -13,7 +13,7 @@ return [
| |
*/ */
'name' => env('APP_NAME', 'Laravel'), 'name' => env('APP_NAME', 'Click_ERP'),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

BIN
public/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
public/assets/js/.DS_Store vendored Normal file

Binary file not shown.

BIN
public/assets/others/.DS_Store vendored Normal file

Binary file not shown.

BIN
public/assets/vendors/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/autosize/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/bootstrap/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/bootstrap/dist/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/bootstrap/less/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/dropify/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/dropzone/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/dropzone/dist/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/fastclick/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/iCheck/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/jquery-knob/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/jquery/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/jquery/src/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/jquery/src/css/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/moment/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/moment/src/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/moment/src/lib/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/nprogress/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/pdfmake/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/pnotify/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/raphael/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/raphael/dev/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/select2/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/select2/docs/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/select2/src/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/select2/src/js/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/select2/tests/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/starrr/.DS_Store vendored Executable file

Binary file not shown.

BIN
public/assets/vendors/switchery/.DS_Store vendored Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/assets/vendors/transitionize/.DS_Store vendored Executable file

Binary file not shown.

BIN
resources/.DS_Store vendored Executable file

Binary file not shown.

BIN
resources/lang/.DS_Store vendored Normal file

Binary file not shown.

BIN
resources/views/.DS_Store vendored Executable file

Binary file not shown.

BIN
resources/views/client/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -11,6 +11,7 @@
<th class="column-title">Status</th> <th class="column-title">Status</th>
<th class="column-title">Remarks</th> <th class="column-title">Remarks</th>
<th class="column-title">Launch Date</th> <th class="column-title">Launch Date</th>
<th class="column-title">Renewal Date</th>
<th class="column-title no-link last"><span class="nobr">Action</span> <th class="column-title no-link last"><span class="nobr">Action</span>
</th> </th>
@@ -31,6 +32,14 @@
<td class="mes-td col-md-1">{{ $row->status }}</td> <td class="mes-td col-md-1">{{ $row->status }}</td>
<td class="mes-td col-md-2">{{ $row->remarks }}</td> <td class="mes-td col-md-2">{{ $row->remarks }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td>
<td class="mes-td col-md-1" style="width: 100px;">
@if($row->expiry_date == false)
{{ "No found"}}
@else
{{ date('d-m-Y', strtotime($row->expiry_date)) }}
@endif
</td>
<td class="last col-md-1" style="width: 100px;"> <td class="last col-md-1" style="width: 100px;">
<span> <span>
<a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a> <a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a>

View File

@@ -10,7 +10,7 @@
<th class="column-title">Toll Free</th> <th class="column-title">Toll Free</th>
<th class="column-title">Status</th> <th class="column-title">Status</th>
<th class="column-title">Launch Date</th> <th class="column-title">Launch Date</th>
<th class="column-title">Expiry Date</th> <th class="column-title">Renewal Date</th>
<th class="column-title no-link last"><span class="nobr">Action</span> <th class="column-title no-link last"><span class="nobr">Action</span>
</th> </th>
@@ -30,7 +30,13 @@
<td class="mes-td col-md-1">{{ $row->toll_free }}</td> <td class="mes-td col-md-1">{{ $row->toll_free }}</td>
<td class="mes-td col-md-1">{{ $row->status }}</td> <td class="mes-td col-md-1">{{ $row->status }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->expiry_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">
@if($row->expiry_date == false)
{{ "No found"}}
@else
{{ date('d-m-Y', strtotime($row->expiry_date)) }}
@endif
</td>
<td class="last col-md-1" style="width: 100px;"> <td class="last col-md-1" style="width: 100px;">
<span> <span>
<a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a> <a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a>

View File

@@ -30,9 +30,16 @@
<td class="mes-td col-md-2">{{ $row->network }}</td> <td class="mes-td col-md-2">{{ $row->network }}</td>
<td class="mes-td col-md-1">{{ $row->toll_free }}</td> <td class="mes-td col-md-1">{{ $row->toll_free }}</td>
<td class="mes-td col-md-1">{{ $row->status }}</td> <td class="mes-td col-md-1">{{ $row->status }}</td>
<td class="mes-td col-md-2">{{ $row->remarks }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->expiry_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">
@if($row->expiry_date == false)
{{ "No found"}}
@else
{{ date('d-m-Y', strtotime($row->expiry_date)) }}
@endif
</td>
<td class="last col-md-1" style="width: 100px;"> <td class="last col-md-1" style="width: 100px;">
<span> <span>
<a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a> <a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a>

View File

@@ -30,7 +30,13 @@
<td class="mes-td col-md-1">{{ $row->toll_free }}</td> <td class="mes-td col-md-1">{{ $row->toll_free }}</td>
<td class="mes-td col-md-1">{{ $row->status }}</td> <td class="mes-td col-md-1">{{ $row->status }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->launch_date)) }}</td>
<td class="mes-td col-md-1" style="width: 100px;">{{ date('d-m-Y', strtotime($row->exipiry_date)) }}</td> <td class="mes-td col-md-1" style="width: 100px;">
@if($row->expiry_date == false)
{{ "No found"}}
@else
{{ date('d-m-Y', strtotime($row->expiry_date)) }}
@endif
</td>
<td class="last col-md-1" style="width: 100px;"> <td class="last col-md-1" style="width: 100px;">
<span> <span>
<a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a> <a href="" class="btn btn-xs btn-primary"><i class="fa fa-edit"></i></a>

Some files were not shown because too many files have changed in this diff Show More