diff --git a/app/Http/Controllers/Admin/UserManagementController.php b/app/Http/Controllers/Admin/UserManagementController.php index dbb0292..55d639c 100644 --- a/app/Http/Controllers/Admin/UserManagementController.php +++ b/app/Http/Controllers/Admin/UserManagementController.php @@ -23,10 +23,8 @@ class UserManagementController extends Controller public function index(Request $request): JsonResponse { - $query = User::query()->where('username', "<>", "witel"); - $data = DataTableFacade::run( - $query, + User::query()->where('username', "<>", "witel"), $request, allowedFilters: array('id', 'full_name', 'username', 'province_id', 'phone_number', 'national_id', 'telephone_id'), allowedSortings: array('id', 'full_name', 'username', 'province_id', 'phone_number', 'national_id', 'telephone_id'), @@ -41,10 +39,6 @@ class UserManagementController extends Controller DB::transaction(function () use ($request) { $province = Province::query()->find($request->province_id); - $avatar = $request->avatar - ? FileFacade::save($request->file('avatar'), 'users/avatars/' . $request->username) - : null; - $user = User::query()->create([ 'full_name' => $request->full_name, 'username' => $request->username, @@ -55,7 +49,6 @@ class UserManagementController extends Controller 'province_id' => $province->id, 'province_fa' => $province->name, 'password' => Hash::make($request->password), - 'avatar' => $avatar, 'email' => $request->email, 'telephone_id' => $request->telephone_id, ]); @@ -86,6 +79,7 @@ class UserManagementController extends Controller 'province_id' => $province->id, 'province_fa' => $province->name, 'email' => $request->email, + 'telephone_id' => $request->telephone_id, ]); $user->syncRoles(Role::query()->find($request->role_id)); diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index f9a7ab6..c566d73 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -33,15 +33,11 @@ class AuthController extends Controller { Auth::user()->update([ 'last_login' => now(), - 'telephone_id' => $request->telephone_id, ]); } public function logout(Request $request): JsonResponse { - Auth::user()->update([ - 'telephone_id' => null, - ]); Auth::guard()->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); diff --git a/app/Http/Controllers/CallController.php b/app/Http/Controllers/CallController.php index 14242d7..6cf67ba 100644 --- a/app/Http/Controllers/CallController.php +++ b/app/Http/Controllers/CallController.php @@ -105,7 +105,7 @@ class CallController extends Controller $event_name = Event::query()->find($event_id)->first()->name; - CallHistory::create([ + CallHistory::query()->create([ 'call_id' => $call->id, 'telephone_id' => $call->telephone_id, 'caller_phone_number' => $call->caller_phone_number, diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index becd223..fb6053f 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -21,6 +21,7 @@ class ProfileController extends Controller { return $this->successResponse(new UserResource(auth()->user())); } + public function changePassword(ChangePasswordRequest $request): JsonResponse { $user = auth()->user(); diff --git a/app/Http/Controllers/Setad/CumulativeCentersStatsController.php b/app/Http/Controllers/Setad/CumulativeCentersStatsController.php new file mode 100644 index 0000000..6cea4ba --- /dev/null +++ b/app/Http/Controllers/Setad/CumulativeCentersStatsController.php @@ -0,0 +1,23 @@ +json($data); + } +} diff --git a/app/Http/Controllers/Setad/CumulativeKeypressStatsController.php b/app/Http/Controllers/Setad/CumulativeKeypressStatsController.php new file mode 100644 index 0000000..47cdec0 --- /dev/null +++ b/app/Http/Controllers/Setad/CumulativeKeypressStatsController.php @@ -0,0 +1,23 @@ +json($data); + } +} diff --git a/app/Http/Controllers/Setad/CumulativeVoiceRatingStatsController.php b/app/Http/Controllers/Setad/CumulativeVoiceRatingStatsController.php new file mode 100644 index 0000000..15802b4 --- /dev/null +++ b/app/Http/Controllers/Setad/CumulativeVoiceRatingStatsController.php @@ -0,0 +1,23 @@ +json($data); + } +} diff --git a/app/Http/Requests/Admin/UserManagement/StoreRequest.php b/app/Http/Requests/Admin/UserManagement/StoreRequest.php index f828f54..cc34bdb 100644 --- a/app/Http/Requests/Admin/UserManagement/StoreRequest.php +++ b/app/Http/Requests/Admin/UserManagement/StoreRequest.php @@ -26,14 +26,14 @@ class StoreRequest extends FormRequest 'full_name' => 'required|max:255|string', 'username' => 'required|unique:users,username|string', 'position' => 'required', - 'gender' => 'required|in:male,female', + 'gender' => 'required|in:1,2', 'national_id' => 'required|digits:10|unique:users,national_id', 'phone_number' => 'required|numeric|digits:11|unique:users,phone_number|regex:/^09\d{9}$/', 'province_id' => 'required|exists:provinces,id', 'password' => 'required|regex:/^(?=.*[a-zA-Z])(?=.*\d).+$/|min:8', 'role_id' => 'required|exists:roles,id', - 'avatar' => 'mimes:png,jpg,pdf|max:2048', 'email' => 'email|unique:users,email', + 'telephone_id' => 'required|numeric|unique:users,telephone_id', ]; } } diff --git a/app/Http/Requests/Admin/UserManagement/UpdateRequest.php b/app/Http/Requests/Admin/UserManagement/UpdateRequest.php index 436995c..28590a0 100644 --- a/app/Http/Requests/Admin/UserManagement/UpdateRequest.php +++ b/app/Http/Requests/Admin/UserManagement/UpdateRequest.php @@ -39,6 +39,7 @@ class UpdateRequest extends FormRequest 'role_id' => 'required|exists:roles,id', 'avatar' => 'mimes:png,jpg,pdf|max:2048', 'email' => ['email', Rule::unique('users', 'email')->ignore($this->user->id)], + 'telephone_id' => ['required', 'numeric', Rule::unique('users', 'telephone_id')->ignore($this->user->id)], ]; } } diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php index 3c57153..b1b0a94 100644 --- a/app/Http/Requests/Auth/LoginRequest.php +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -27,25 +27,6 @@ class LoginRequest extends FormRequest return [ 'username' => 'required|exists:users,username', 'password' => 'required|string', - 'telephone_id' => 'required|string' - ]; - } - - public function after(): array - { - return [ - function (Validator $validator) { - $user = User::query()->where('telephone_id', '=', $this->telephone_id); - if ($user->exists()) { - if ($user->first()->is_online) { - $validator->errors()->add( - 'telephone_id', - __('messages.telephone_id_is_in_use') - ); - } - $user->first()->update(['telephone_id' => null]); - } - } ]; } } diff --git a/app/Http/Resources/Admin/UserResource.php b/app/Http/Resources/Admin/UserResource.php index 7f81196..a3b05c4 100644 --- a/app/Http/Resources/Admin/UserResource.php +++ b/app/Http/Resources/Admin/UserResource.php @@ -26,6 +26,7 @@ class UserResource extends JsonResource 'province_id' => $this->province_id, 'province_name' => $this->province_name, 'role' => $this->roles->pluck('id', 'name_fa'), + 'permissions' => $this->getPermissionNames(), 'telephone_id' => $this->telephone_id ]; } diff --git a/app/Models/CountryCallsReport.php b/app/Models/CountryCallsReport.php new file mode 100644 index 0000000..7112254 --- /dev/null +++ b/app/Models/CountryCallsReport.php @@ -0,0 +1,12 @@ + */ + use HasFactory; +} diff --git a/app/Models/CountryKeypressReport.php b/app/Models/CountryKeypressReport.php new file mode 100644 index 0000000..f8dbbfc --- /dev/null +++ b/app/Models/CountryKeypressReport.php @@ -0,0 +1,12 @@ + */ + use HasFactory; +} diff --git a/app/Models/CountryMessageRatingReport.php b/app/Models/CountryMessageRatingReport.php new file mode 100644 index 0000000..3e7501a --- /dev/null +++ b/app/Models/CountryMessageRatingReport.php @@ -0,0 +1,12 @@ + */ + use HasFactory; +} diff --git a/app/Models/CountryVoiceRatingReport.php b/app/Models/CountryVoiceRatingReport.php new file mode 100644 index 0000000..bf3b2c6 --- /dev/null +++ b/app/Models/CountryVoiceRatingReport.php @@ -0,0 +1,12 @@ + */ + use HasFactory; +} diff --git a/app/Models/ExpertPerformance.php b/app/Models/ExpertPerformance.php new file mode 100644 index 0000000..a6aa1f8 --- /dev/null +++ b/app/Models/ExpertPerformance.php @@ -0,0 +1,12 @@ + */ + use HasFactory; +} diff --git a/database/factories/CountryCallsReportFactory.php b/database/factories/CountryCallsReportFactory.php new file mode 100644 index 0000000..2633de0 --- /dev/null +++ b/database/factories/CountryCallsReportFactory.php @@ -0,0 +1,42 @@ + + */ +class CountryCallsReportFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { +// $this->faker->randomElement(Province::all()->pluck('id')->toArray()) + return [ + 'province_id' => Province::factory(), + 'province_name' => function (array $attributes) { + return Province::query()->find($attributes['province_id'])->name; + }, + 'date' => $this->faker->date(), + 'total' => $this->faker->numberBetween(1000, 10000), + 'route_to_teh4' => $this->faker->numberBetween(10, 100), + 'route_to_teh5' => $this->faker->numberBetween(10, 100), + 'route_to_os4' => $this->faker->numberBetween(10, 100), + 'route_to_os5' => $this->faker->numberBetween(10, 100), + 'answered' => $this->faker->numberBetween(10, 100), + 'answered_over10' => $this->faker->numberBetween(10, 100), + 'missed' => $this->faker->numberBetween(10, 100), + 'dnd' => $this->faker->numberBetween(10, 100), + 'transferred' => $this->faker->numberBetween(10, 100), + 'talked_to_operator' => $this->faker->numberBetween(10, 100), + 'average_calls_time' => $this->faker->numberBetween(10, 100), + 'fails' => $this->faker->numberBetween(10, 100), + ]; + } +} diff --git a/database/factories/CountryKeypressReportFactory.php b/database/factories/CountryKeypressReportFactory.php new file mode 100644 index 0000000..63fd69d --- /dev/null +++ b/database/factories/CountryKeypressReportFactory.php @@ -0,0 +1,44 @@ + + */ +class CountryKeypressReportFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { +// $this->faker->randomElement(Province::all()->pluck('id')->toArray()) + return [ + 'province_id' => Province::factory(), + 'province_name' => function (array $attributes) { + return Province::query()->find($attributes['province_id'])->name; + }, + 'date' => $this->faker->date(), + 'total' => $this->faker->numberBetween(1000, 10000), + 'key1' => $this->faker->numberBetween(10, 100), + 'key2' => $this->faker->numberBetween(10, 100), + 'key3' => $this->faker->numberBetween(10, 100), + 'key4' => $this->faker->numberBetween(10, 100), + 'key5' => $this->faker->numberBetween(10, 100), + 'key6' => $this->faker->numberBetween(10, 100), + 'key9' => $this->faker->numberBetween(10, 100), + 'key4_1' => $this->faker->numberBetween(10, 100), + 'key4_2' => $this->faker->numberBetween(10, 100), + 'key5_1' => $this->faker->numberBetween(10, 100), + 'key5_2' => $this->faker->numberBetween(10, 100), + 'key2_1' => $this->faker->numberBetween(10, 100), + 'key2_2' => $this->faker->numberBetween(10, 100), + 'key2_3' => $this->faker->numberBetween(10, 100), + ]; + } +} diff --git a/database/factories/CountryMessageRatingReportFactory.php b/database/factories/CountryMessageRatingReportFactory.php new file mode 100644 index 0000000..475dd76 --- /dev/null +++ b/database/factories/CountryMessageRatingReportFactory.php @@ -0,0 +1,35 @@ + + */ +class CountryMessageRatingReportFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { +// $this->faker->randomElement(Province::all()->pluck('id')->toArray()) + return [ + 'province_id' => Province::factory(), + 'province_name' => function (array $attributes) { + return Province::query()->find($attributes['province_id'])->name; + }, + 'date' => $this->faker->date(), + 'very_good' => $this->faker->numberBetween(10, 100), + 'good' => $this->faker->numberBetween(10, 100), + 'average' => $this->faker->numberBetween(10, 100), + 'bad' => $this->faker->numberBetween(10, 100), + 'very_bad' => $this->faker->numberBetween(10, 100), + 'wrong_code' => $this->faker->numberBetween(10, 100), + ]; + } +} diff --git a/database/factories/CountryVoiceRatingReportFactory.php b/database/factories/CountryVoiceRatingReportFactory.php new file mode 100644 index 0000000..e794741 --- /dev/null +++ b/database/factories/CountryVoiceRatingReportFactory.php @@ -0,0 +1,35 @@ + + */ +class CountryVoiceRatingReportFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { +// $this->faker->randomElement(Province::all()->pluck('id')->toArray()) + return [ + 'province_id' => Province::factory(), + 'province_name' => function (array $attributes) { + return Province::query()->find($attributes['province_id'])->name; + }, + 'date' => $this->faker->date(), + 'voice_quality_ok' => $this->faker->numberBetween(10, 100), + 'message_quality_ok' => $this->faker->numberBetween(10, 100), + 'voice_format_ok' => $this->faker->numberBetween(10, 100), + 'voice_quality_nok' => $this->faker->numberBetween(10, 100), + 'message_quality_nok' => $this->faker->numberBetween(10, 100), + 'voice_format_nok' => $this->faker->numberBetween(10, 100), + ]; + } +} diff --git a/database/factories/ExpertPerformanceFactory.php b/database/factories/ExpertPerformanceFactory.php new file mode 100644 index 0000000..2ee7b2e --- /dev/null +++ b/database/factories/ExpertPerformanceFactory.php @@ -0,0 +1,23 @@ + + */ +class ExpertPerformanceFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/ProvinceFactory.php b/database/factories/ProvinceFactory.php index 891ea6e..49e3b46 100644 --- a/database/factories/ProvinceFactory.php +++ b/database/factories/ProvinceFactory.php @@ -17,9 +17,11 @@ class ProvinceFactory extends Factory public function definition(): array { return [ - 'name' => $this->faker->state, + 'name' => $this->faker->name(), 'lat' => $this->faker->latitude, 'lon' => $this->faker->longitude, + 'ip' => $this->faker->ipv4, + 'camera_ip' => $this->faker->ipv4, ]; } } diff --git a/database/migrations/2025_07_21_055258_create_country_calls_reports_table.php b/database/migrations/2025_07_21_055258_create_country_calls_reports_table.php new file mode 100644 index 0000000..c4f5e08 --- /dev/null +++ b/database/migrations/2025_07_21_055258_create_country_calls_reports_table.php @@ -0,0 +1,43 @@ +id(); + $table->foreignId('province_id')->constrained('provinces'); + $table->string('province_name'); + $table->date('date'); + $table->integer('total'); + $table->integer('route_to_teh4'); + $table->integer('route_to_teh5'); + $table->integer('route_to_os4'); + $table->integer('route_to_os5'); + $table->integer('answered'); + $table->integer('answered_over10'); + $table->integer('missed'); + $table->integer('dnd'); + $table->integer('transferred'); + $table->integer('talked_to_operator'); + $table->float('average_calls_time'); + $table->integer('fails'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('country_calls_reports'); + } +}; diff --git a/database/migrations/2025_07_21_061758_create_country_keypress_reports_table.php b/database/migrations/2025_07_21_061758_create_country_keypress_reports_table.php new file mode 100644 index 0000000..71154c3 --- /dev/null +++ b/database/migrations/2025_07_21_061758_create_country_keypress_reports_table.php @@ -0,0 +1,45 @@ +id(); + $table->foreignId('province_id')->constrained('provinces'); + $table->string('province_name'); + $table->date('date'); + $table->integer('total'); + $table->integer('key1'); + $table->integer('key2'); + $table->integer('key3'); + $table->integer('key4'); + $table->integer('key5'); + $table->integer('key6'); + $table->integer('key9'); + $table->integer('key4_1'); + $table->integer('key4_2'); + $table->integer('key5_1'); + $table->integer('key5_2'); + $table->integer('key2_1'); + $table->integer('key2_2'); + $table->integer('key2_3'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('country_keypress_reports'); + } +}; diff --git a/database/migrations/2025_07_21_062738_create_country_voice_rating_reports_table.php b/database/migrations/2025_07_21_062738_create_country_voice_rating_reports_table.php new file mode 100644 index 0000000..b31a6c5 --- /dev/null +++ b/database/migrations/2025_07_21_062738_create_country_voice_rating_reports_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('province_id')->constrained('provinces'); + $table->string('province_name'); + $table->date('date'); + $table->integer('voice_quality_ok'); + $table->integer('message_quality_ok'); + $table->integer('voice_format_ok'); + $table->integer('voice_quality_nok'); + $table->integer('message_quality_nok'); + $table->integer('voice_format_nok'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('country_voice_rating_reports'); + } +}; diff --git a/database/migrations/2025_07_21_072211_create_country_message_rating_reports_table.php b/database/migrations/2025_07_21_072211_create_country_message_rating_reports_table.php new file mode 100644 index 0000000..1f92bfb --- /dev/null +++ b/database/migrations/2025_07_21_072211_create_country_message_rating_reports_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('province_id')->constrained('provinces'); + $table->string('province_name'); + $table->date('date'); + $table->integer('very_good'); + $table->integer('good'); + $table->integer('average'); + $table->integer('bad'); + $table->integer('very_bad'); + $table->integer('wrong_code'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('country_message_rating_reports'); + } +}; diff --git a/database/migrations/2025_07_21_094121_add_column_to_provinces_table.php b/database/migrations/2025_07_21_094121_add_column_to_provinces_table.php new file mode 100644 index 0000000..67752ab --- /dev/null +++ b/database/migrations/2025_07_21_094121_add_column_to_provinces_table.php @@ -0,0 +1,29 @@ +string('ip')->nullable(); + $table->string('camera_ip')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('provinces', function (Blueprint $table) { + $table->dropColumn(['ip', 'camera_ip']); + }); + } +}; diff --git a/database/migrations/2025_07_23_061700_create_expert_performances_table.php b/database/migrations/2025_07_23_061700_create_expert_performances_table.php new file mode 100644 index 0000000..2a0b866 --- /dev/null +++ b/database/migrations/2025_07_23_061700_create_expert_performances_table.php @@ -0,0 +1,27 @@ +id(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('expert_performances'); + } +}; diff --git a/database/seeders/CountryCallsReportSeeder.php b/database/seeders/CountryCallsReportSeeder.php new file mode 100644 index 0000000..ac41790 --- /dev/null +++ b/database/seeders/CountryCallsReportSeeder.php @@ -0,0 +1,18 @@ +count(10)->create(); + } +} diff --git a/database/seeders/CountryKeypressReportSeeder.php b/database/seeders/CountryKeypressReportSeeder.php new file mode 100644 index 0000000..a4e46c1 --- /dev/null +++ b/database/seeders/CountryKeypressReportSeeder.php @@ -0,0 +1,18 @@ +count(10)->create(); + } +} diff --git a/database/seeders/CountryMessageRatingReportSeeder.php b/database/seeders/CountryMessageRatingReportSeeder.php new file mode 100644 index 0000000..08c8eed --- /dev/null +++ b/database/seeders/CountryMessageRatingReportSeeder.php @@ -0,0 +1,18 @@ +count(5)->create(); + } +} diff --git a/database/seeders/CountryVoiceRatingReportSeeder.php b/database/seeders/CountryVoiceRatingReportSeeder.php new file mode 100644 index 0000000..007e534 --- /dev/null +++ b/database/seeders/CountryVoiceRatingReportSeeder.php @@ -0,0 +1,18 @@ +count(10)->create(); + } +} diff --git a/database/seeders/ExpertPerformanceSeeder.php b/database/seeders/ExpertPerformanceSeeder.php new file mode 100644 index 0000000..ea2f8ea --- /dev/null +++ b/database/seeders/ExpertPerformanceSeeder.php @@ -0,0 +1,17 @@ +group(function () { Route::post('/{role}', 'update')->name('update'); Route::delete('/{role}', 'destroy')->name('destroy'); }); + + Route::middleware('auth') + ->prefix('cumulative_stats') + ->name('cumulativeStats.') + ->controller(CumulativeCentersStatsController::class) + ->group(function () { + Route::get('/', 'index')->name('index'); + }); + + Route::middleware('auth') + ->prefix('cumulative_voice_ratings') + ->name('cumulativeVoiceRatings.') + ->controller(CumulativeVoiceRatingStatsController::class) + ->group(function () { + Route::get('/', 'index')->name('index'); + }); + + Route::middleware('auth') + ->prefix('cumulative_keypress_stats') + ->name('cumulativeKeypressStats.') + ->controller(CumulativeKeypressStatsController::class) + ->group(function () { + Route::get('/', 'index')->name('index'); + }); });