Compare commits
35 Commits
feature/ch
...
fix/sync-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22ee7502ad | ||
|
|
2f3bc172eb | ||
|
|
bba932b2ba | ||
|
|
3f5d0eb1cd | ||
|
|
1f33d0de4e | ||
|
|
86d694bcac | ||
|
|
cb5a3243fc | ||
|
|
15210a56ee | ||
|
|
a08f2cb2b7 | ||
|
|
632433c496 | ||
|
|
5b4780495e | ||
|
|
0a7012a57c | ||
|
|
435a19346b | ||
|
|
8fcf8859d6 | ||
|
|
43a246d234 | ||
|
|
d6d0acf8fb | ||
|
|
b4ec7a9d25 | ||
|
|
5203babe11 | ||
|
|
c0faafdbd7 | ||
|
|
c5e3fdd915 | ||
|
|
572b86299c | ||
|
|
cdd84d02da | ||
|
|
ee1a395c75 | ||
|
|
3bfcaddba4 | ||
|
|
9ea7e96af1 | ||
|
|
e0d11af7d2 | ||
|
|
f5790cda94 | ||
|
|
f3db3783f9 | ||
|
|
101e76c0fa | ||
|
|
4cc698a623 | ||
|
|
544ad1db46 | ||
|
|
30ca819aa1 | ||
|
|
b0bab784d1 | ||
|
|
01fda22c89 | ||
|
|
de300c2c32 |
85
README.md
85
README.md
@@ -1,66 +1,37 @@
|
|||||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
# Usage icon
|
||||||
|
|
||||||
<p align="center">
|
search or pick icon in <a href="https://icon-sets.iconify.design/mingcute/?keyword=mingcute">here</a>
|
||||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
|
||||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
## About Laravel
|
# Set up queue for running automatically
|
||||||
|
|
||||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
- Install Supervisor
|
||||||
|
|
||||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
```
|
||||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
sudo apt update && sudo apt install supervisor -y
|
||||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
```
|
||||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
|
||||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
|
||||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
|
||||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
|
||||||
|
|
||||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
- Create Supervisor Config
|
||||||
|
|
||||||
## Learning Laravel
|
```
|
||||||
|
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
|
||||||
|
|
||||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
[program:laravel-worker]
|
||||||
|
process_name=%(program_name)s_%(process_num)02d
|
||||||
|
command=php /path-to-your-project/artisan queue:work --tries=3 --timeout=600
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
numprocs=1
|
||||||
|
user=www-data
|
||||||
|
redirect_stderr=true
|
||||||
|
stdout_logfile=/var/log/supervisor/laravel-worker.log
|
||||||
|
```
|
||||||
|
|
||||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
- Reload Supervisor
|
||||||
|
|
||||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
```
|
||||||
|
sudo supervisorctl reread
|
||||||
## Laravel Sponsors
|
sudo supervisorctl update
|
||||||
|
sudo supervisorctl start laravel-worker
|
||||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
|
sudo supervisorctl restart laravel-worker
|
||||||
|
sudo supervisorctl status
|
||||||
### Premium Partners
|
```
|
||||||
|
|
||||||
- **[Vehikl](https://vehikl.com/)**
|
|
||||||
- **[Tighten Co.](https://tighten.co)**
|
|
||||||
- **[WebReinvent](https://webreinvent.com/)**
|
|
||||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
|
||||||
- **[64 Robots](https://64robots.com)**
|
|
||||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
|
||||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
|
||||||
- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
|
|
||||||
- **[Jump24](https://jump24.co.uk)**
|
|
||||||
- **[Redberry](https://redberry.international/laravel/)**
|
|
||||||
- **[Active Logic](https://activelogic.com)**
|
|
||||||
- **[byte5](https://byte5.de)**
|
|
||||||
- **[OP.GG](https://op.gg)**
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
|
||||||
|
|
||||||
## Security Vulnerabilities
|
|
||||||
|
|
||||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Jobs\SyncronizeSIMBG;
|
||||||
use App\Services\ServiceSIMBG;
|
use App\Services\ServiceSIMBG;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use \Illuminate\Support\Facades\Log;
|
use \Illuminate\Support\Facades\Log;
|
||||||
@@ -28,13 +29,12 @@ class ExecuteScraping extends Command
|
|||||||
|
|
||||||
private $service_simbg;
|
private $service_simbg;
|
||||||
|
|
||||||
public function __construct(ServiceSIMBG $service_simbg){
|
public function __construct(){
|
||||||
$this->service_simbg = $service_simbg;
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
SyncronizeSIMBG::dispatch();
|
||||||
Log::info("running scheduler daily scraping");
|
Log::info("running scheduler daily scraping");
|
||||||
$this->service_simbg->syncTaskList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Resources\BigdataResumeResource;
|
||||||
use App\Models\BigdataResume;
|
use App\Models\BigdataResume;
|
||||||
use App\Models\DataSetting;
|
use App\Models\DataSetting;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -18,11 +19,12 @@ class BigDataResumeController extends Controller
|
|||||||
try{
|
try{
|
||||||
$filterDate = $request->get("filterByDate");
|
$filterDate = $request->get("filterByDate");
|
||||||
|
|
||||||
// If filterByDate is "latest" or empty, get the most recent record
|
|
||||||
if (!$filterDate || $filterDate === "latest") {
|
if (!$filterDate || $filterDate === "latest") {
|
||||||
$big_data_resume = BigdataResume::latest()->first();
|
$big_data_resume = BigdataResume::where('year', now()->year)->latest()->first();
|
||||||
|
if (!$big_data_resume) {
|
||||||
|
return $this->response_empty_resume();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Filter by specific date
|
|
||||||
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate)
|
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate)
|
||||||
->orderBy('id', 'desc')
|
->orderBy('id', 'desc')
|
||||||
->first();
|
->first();
|
||||||
@@ -37,15 +39,19 @@ class BigDataResumeController extends Controller
|
|||||||
return response()->json(['message' => 'No data setting found']);
|
return response()->json(['message' => 'No data setting found']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value);
|
function cleanNumber($value) {
|
||||||
$tata_ruang = floatval(optional($data_settings->where('key', 'TATA_RUANG')->first())->value);
|
return floatval(str_replace('.', '', $value));
|
||||||
$realisasi_terbit_pbg_sum = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_SUM')->first())->value);
|
}
|
||||||
$realisasi_terbit_pbg_count = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_COUNT')->first())->value);
|
|
||||||
$menuggu_klik_dpmptsp_sum = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_SUM')->first())->value);
|
|
||||||
$menuggu_klik_dpmptsp_count = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_COUNT')->first())->value);
|
|
||||||
$proses_dinas_teknis_sum = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_SUM')->first())->value);
|
|
||||||
$proses_dinas_teknis_count = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_COUNT')->first())->value);
|
|
||||||
|
|
||||||
|
$target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value);
|
||||||
|
$realisasi_terbit_pbg_sum = cleanNumber(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_SUM')->first())->value);
|
||||||
|
$realisasi_terbit_pbg_count = cleanNumber(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_COUNT')->first())->value);
|
||||||
|
$menunggu_klik_dpmptsp_sum = cleanNumber(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_SUM')->first())->value);
|
||||||
|
$menunggu_klik_dpmptsp_count = cleanNumber(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_COUNT')->first())->value);
|
||||||
|
$proses_dinas_teknis_sum = cleanNumber(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_SUM')->first())->value);
|
||||||
|
$proses_dinas_teknis_count = cleanNumber(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_COUNT')->first())->value);
|
||||||
|
|
||||||
|
$tata_ruang = $big_data_resume->spatial_sum;
|
||||||
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
|
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
|
||||||
|
|
||||||
// percentage kekurangan potensi
|
// percentage kekurangan potensi
|
||||||
@@ -81,8 +87,8 @@ class BigDataResumeController extends Controller
|
|||||||
? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
||||||
|
|
||||||
// percentage menunggu klik dpmptsp
|
// percentage menunggu klik dpmptsp
|
||||||
$menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menuggu_klik_dpmptsp_sum > 0
|
$menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menunggu_klik_dpmptsp_sum > 0
|
||||||
? round(($menuggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
? round(($menunggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
|
||||||
|
|
||||||
// percentage proses_dinas_teknis
|
// percentage proses_dinas_teknis
|
||||||
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0
|
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0
|
||||||
@@ -94,7 +100,8 @@ class BigDataResumeController extends Controller
|
|||||||
'percentage' => 100,
|
'percentage' => 100,
|
||||||
],
|
],
|
||||||
'tata_ruang' => [
|
'tata_ruang' => [
|
||||||
'sum' => $tata_ruang,
|
'sum' => $big_data_resume->spatial_sum,
|
||||||
|
'count' => $big_data_resume->spatial_count,
|
||||||
'percentage' => $tata_ruang_percentage,
|
'percentage' => $tata_ruang_percentage,
|
||||||
],
|
],
|
||||||
'kekurangan_potensi' => [
|
'kekurangan_potensi' => [
|
||||||
@@ -132,8 +139,8 @@ class BigDataResumeController extends Controller
|
|||||||
'percentage' => $realisasi_terbit_percentage
|
'percentage' => $realisasi_terbit_percentage
|
||||||
],
|
],
|
||||||
'menunggu_klik_dpmptsp' => [
|
'menunggu_klik_dpmptsp' => [
|
||||||
'sum' => $menuggu_klik_dpmptsp_sum,
|
'sum' => $menunggu_klik_dpmptsp_sum,
|
||||||
'count' => $menuggu_klik_dpmptsp_count,
|
'count' => $menunggu_klik_dpmptsp_count,
|
||||||
'percentage' => $menunggu_klik_dpmptsp_percentage
|
'percentage' => $menunggu_klik_dpmptsp_percentage
|
||||||
],
|
],
|
||||||
'proses_dinas_teknis' => [
|
'proses_dinas_teknis' => [
|
||||||
@@ -148,36 +155,22 @@ class BigDataResumeController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*/
|
public function bigdata_report(Request $request){
|
||||||
public function store(Request $request)
|
try{
|
||||||
{
|
$query = BigdataResume::query()->orderBy('id', 'desc');
|
||||||
//
|
|
||||||
|
if($request->filled('search')){
|
||||||
|
$query->where('name', 'LIKE', '%'.$request->input('search').'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
$query = $query->paginate(config('app.paginate_per_page', 50));
|
||||||
* Display the specified resource.
|
return BigdataResumeResource::collection($query)->response()->getData(true);
|
||||||
*/
|
}catch(\Exception $e){
|
||||||
public function show(string $id)
|
Log::error($e->getMessage());
|
||||||
{
|
return response()->json(['message' => 'Error when fetching data'], 500);
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*/
|
|
||||||
public function update(Request $request, string $id)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*/
|
|
||||||
public function destroy(string $id)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function response_empty_resume(){
|
private function response_empty_resume(){
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use App\Models\BusinessOrIndustry;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Maatwebsite\Excel\Facades\Excel;
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
use \Illuminate\Support\Facades\Validator;
|
use \Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Http\Requests\ExcelUploadRequest;
|
||||||
class BusinessOrIndustriesController extends Controller
|
class BusinessOrIndustriesController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +31,7 @@ class BusinessOrIndustriesController extends Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($query->paginate());
|
return response()->json($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,29 +80,15 @@ class BusinessOrIndustriesController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload(Request $request){
|
public function upload(ExcelUploadRequest $request){
|
||||||
|
try {
|
||||||
if ($request->hasFile('file')) {
|
if(!$request->hasFile('file')){
|
||||||
$file = $request->file('file');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validasi file
|
|
||||||
$validator = Validator::make($request->all(), [
|
|
||||||
'file' => 'required|mimes:xlsx,xls|max:102400', // Max 100MB
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($validator->fails()) {
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'File validation failed.',
|
'error' => 'No file provided'
|
||||||
'errors' => $validator->errors()
|
|
||||||
], 400);
|
], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// Ambil file dari request
|
|
||||||
$file = $request->file('file');
|
$file = $request->file('file');
|
||||||
|
|
||||||
// Menggunakan Laravel Excel untuk mengimpor file
|
|
||||||
Excel::import(new BusinessIndustriesImport, $file);
|
Excel::import(new BusinessIndustriesImport, $file);
|
||||||
|
|
||||||
// Jika sukses, kembalikan respons sukses
|
// Jika sukses, kembalikan respons sukses
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Services\OpenAIService;
|
|||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class ChatbotController extends Controller
|
class ChatbotController extends Controller
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,6 @@ class ChatbotController extends Controller
|
|||||||
|
|
||||||
public function generateText(Request $request)
|
public function generateText(Request $request)
|
||||||
{
|
{
|
||||||
info($request);
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'tab_active' => 'required|string',
|
'tab_active' => 'required|string',
|
||||||
'prompt' => 'required|string',
|
'prompt' => 'required|string',
|
||||||
@@ -33,52 +33,87 @@ class ChatbotController extends Controller
|
|||||||
default => "UNKNOWN",
|
default => "UNKNOWN",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$chatHistory = $request->input('chatHistory');
|
||||||
|
// Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]);
|
||||||
|
|
||||||
if ($main_content === "UNKNOWN") {
|
if ($main_content === "UNKNOWN") {
|
||||||
return response()->json(['response' => 'Invalid tab_active value.'], 400);
|
return response()->json(['response' => 'Invalid tab_active value.'], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
info($main_content);
|
// info($main_content);
|
||||||
|
|
||||||
// Klasifikasi apakah pertanyaan butuh database atau bisa dijawab langsung
|
$queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content, $chatHistory);
|
||||||
$classifyResponse = $this->openAIService->generateClassifyMainContent($request->input('prompt'), $main_content);
|
|
||||||
|
|
||||||
if ($classifyResponse === "DATABASE") {
|
$firstValidation = $this->openAIService->validateSyntaxQuery($queryResponse);
|
||||||
$queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content);
|
$secondValidation = $this->openAIService->validateSyntaxQuery($queryResponse);
|
||||||
if (is_array($queryResponse)) {
|
|
||||||
info('Query Response is an array: ', $queryResponse);
|
|
||||||
} else {
|
|
||||||
info('Query Response is a string: ' . $queryResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validasi query dua kali sebelum eksekusi
|
$formattedResultQuery = "[]";
|
||||||
if (
|
|
||||||
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" &&
|
|
||||||
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID"
|
|
||||||
) {
|
|
||||||
info($queryResponse);
|
|
||||||
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
|
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
|
||||||
$resultQuery = DB::select($queryResponse);
|
$resultQuery = DB::select($queryResponse);
|
||||||
$formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT);
|
$formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT);
|
||||||
|
// info($formattedResultQuery);
|
||||||
|
|
||||||
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
|
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
|
||||||
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
|
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
|
||||||
return response()->json(['response' => $finalGeneratedText]);
|
return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['response' => ''], 400);
|
public function mainGenerateText(Request $request)
|
||||||
|
{
|
||||||
|
// Log hanya data yang relevan
|
||||||
|
info("Received prompt: " . $request->input('prompt'));
|
||||||
|
|
||||||
|
// Validasi input
|
||||||
|
$request->validate([
|
||||||
|
'prompt' => 'required|string',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Panggil service untuk generate text
|
||||||
|
$classifyResponse = $this->openAIService->classifyMainGenerateText($request->input('prompt'));
|
||||||
|
info($classifyResponse);
|
||||||
|
|
||||||
|
// Pastikan hasil klasifikasi valid sebelum melanjutkan
|
||||||
|
$validCategories = [
|
||||||
|
'reklame', 'business_or_industries', 'customers',
|
||||||
|
'pbg', 'retribusi', 'spatial_plannings',
|
||||||
|
'tourisms', 'umkms'
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!in_array($classifyResponse, $validCategories)) {
|
||||||
|
return response()->json([
|
||||||
|
'error' => ''
|
||||||
|
], 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($classifyResponse === "GENERAL") {
|
$chatHistory = $request->input('chatHistory');
|
||||||
$nlpResult = $this->openAIService->generateGeneralText($request->input('prompt'), $main_content);
|
Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]);
|
||||||
|
|
||||||
|
$queryResponse = $this->openAIService->createMainQuery($classifyResponse, $request->input('prompt'), $chatHistory);
|
||||||
|
info($queryResponse);
|
||||||
|
|
||||||
|
$firstValidation = $this->openAIService->validateSyntaxQuery($queryResponse);
|
||||||
|
$secondValidation = $this->openAIService->validateSyntaxQuery($queryResponse);
|
||||||
|
|
||||||
|
$formattedResultQuery = "[]";
|
||||||
|
|
||||||
|
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
|
||||||
|
$queryResult = DB::select($queryResponse);
|
||||||
|
|
||||||
|
$formattedResultQuery = json_encode($queryResult, JSON_PRETTY_PRINT);
|
||||||
|
|
||||||
|
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
|
||||||
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
|
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
|
||||||
return response()->json(['response' => $finalGeneratedText]);
|
|
||||||
|
return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Tangani error dan log exception
|
||||||
|
\Log::error("Error generating text: " . $e->getMessage());
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'error' => ''
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['response' => ''], 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private function classifyContent(string $prompt) {
|
|
||||||
$classifyResponse = $this->openAIService->generateClassifyContent($prompt);
|
|
||||||
return $classifyResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -22,9 +22,9 @@ class CustomersController extends Controller
|
|||||||
if ($request->has('search') &&!empty($request->get('search'))) {
|
if ($request->has('search') &&!empty($request->get('search'))) {
|
||||||
$query = $query->where('nomor_pelanggan', 'LIKE', '%'.$request->get('search').'%')
|
$query = $query->where('nomor_pelanggan', 'LIKE', '%'.$request->get('search').'%')
|
||||||
->orWhere('nama', 'LIKE', '%'.$request->get('search').'%')
|
->orWhere('nama', 'LIKE', '%'.$request->get('search').'%')
|
||||||
->orWhere('kota_palayanan', 'LIKE', '%'.$request->get('search').'%');
|
->orWhere('kota_pelayanan', 'LIKE', '%'.$request->get('search').'%');
|
||||||
}
|
}
|
||||||
return CustomersResource::collection($query->paginate());
|
return CustomersResource::collection($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ImportDatasourceController extends Controller
|
|||||||
$search = $request->get("search");
|
$search = $request->get("search");
|
||||||
$query->where('status', 'like', "%".$search."%");
|
$query->where('status', 'like', "%".$search."%");
|
||||||
}
|
}
|
||||||
return ImportDatasourceResource::collection($query->paginate());
|
return ImportDatasourceResource::collection($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkImportDatasource(){
|
public function checkImportDatasource(){
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Models\Advertisement;
|
|||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
use App\Models\SpatialPlanning;
|
use App\Models\SpatialPlanning;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\TourismBasedKBLI;
|
||||||
|
|
||||||
class LackOfPotentialController extends Controller
|
class LackOfPotentialController extends Controller
|
||||||
{
|
{
|
||||||
@@ -16,11 +17,13 @@ class LackOfPotentialController extends Controller
|
|||||||
$total_reklame = Advertisement::count();
|
$total_reklame = Advertisement::count();
|
||||||
$total_pdam = Customer::count();
|
$total_pdam = Customer::count();
|
||||||
$total_tata_ruang = SpatialPlanning::count();
|
$total_tata_ruang = SpatialPlanning::count();
|
||||||
|
$data_report_tourism = TourismBasedKBLI::all();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'total_reklame' => $total_reklame,
|
'total_reklame' => $total_reklame,
|
||||||
'total_pdam' => $total_pdam,
|
'total_pdam' => $total_pdam,
|
||||||
'total_tata_ruang' => $total_tata_ruang
|
'total_tata_ruang' => $total_tata_ruang,
|
||||||
|
'data_report' => $data_report_tourism,
|
||||||
], 200);
|
], 200);
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class MenusController extends Controller
|
|||||||
$query = $query->where("name", "like", "%".$request->get("search")."%");
|
$query = $query->where("name", "like", "%".$request->get("search")."%");
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($query->paginate());
|
return response()->json($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class RequestAssignmentController extends Controller
|
|||||||
$query = PbgTask::query()->orderBy('id', 'desc');
|
$query = PbgTask::query()->orderBy('id', 'desc');
|
||||||
if($request->has('search') && !empty($request->get("search"))){
|
if($request->has('search') && !empty($request->get("search"))){
|
||||||
$query->where('name', 'LIKE', '%'.$request->get('search').'%')
|
$query->where('name', 'LIKE', '%'.$request->get('search').'%')
|
||||||
->orWhere('registration_number', 'LIKE', '%'.$request->get('search').'%');
|
->orWhere('registration_number', 'LIKE', '%'.$request->get('search').'%')
|
||||||
|
->orWhere('document_number', 'LIKE', '%'.$request->get('search').'%');
|
||||||
}
|
}
|
||||||
return RequestAssignmentResouce::collection($query->paginate());
|
return RequestAssignmentResouce::collection($query->paginate());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class RolesController extends Controller
|
|||||||
$query = $query->where('name', 'like', '%'. $request->get('search') . '%');
|
$query = $query->where('name', 'like', '%'. $request->get('search') . '%');
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($query->paginate());
|
return response()->json($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
|
|||||||
|
|
||||||
use App\Enums\ImportDatasourceStatus;
|
use App\Enums\ImportDatasourceStatus;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Jobs\SyncronizeSIMBG;
|
||||||
use App\Models\ImportDatasource;
|
use App\Models\ImportDatasource;
|
||||||
use App\Traits\GlobalApiResponse;
|
use App\Traits\GlobalApiResponse;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
@@ -23,8 +24,8 @@ class ScrapingController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// run service artisan command
|
// run service artisan command
|
||||||
Artisan::call("app:execute-scraping");
|
SyncronizeSIMBG::dispatch();
|
||||||
return $this->resSuccess("Success execute scraping service please wait");
|
return $this->resSuccess(["message" => "Success execute scraping service on background, check status for more"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class UsersController extends Controller
|
|||||||
if($request->has('search') && !empty($request->get("search"))){
|
if($request->has('search') && !empty($request->get("search"))){
|
||||||
$query->where('name', 'LIKE', '%'.$request->get('search').'%');
|
$query->where('name', 'LIKE', '%'.$request->get('search').'%');
|
||||||
}
|
}
|
||||||
return UserResource::collection($query->paginate());
|
return UserResource::collection($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
}
|
}
|
||||||
public function logout(Request $request){
|
public function logout(Request $request){
|
||||||
$request->user()->tokens()->delete();
|
$request->user()->tokens()->delete();
|
||||||
|
|||||||
48
app/Http/Controllers/BigdataResumesController.php
Normal file
48
app/Http/Controllers/BigdataResumesController.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class BigdataResumesController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('bigdata-resumes.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\ChatbotPimpinan;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ChatbotPimpinanController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('chatbot-pimpinan.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,7 @@ class AdvertisementController extends Controller
|
|||||||
// Pastikan model ditemukan
|
// Pastikan model ditemukan
|
||||||
if (!$modelInstance) {
|
if (!$modelInstance) {
|
||||||
info("AdvertisementController@edit: Model tidak ditemukan.");
|
info("AdvertisementController@edit: Model tidak ditemukan.");
|
||||||
return redirect()->route('advertisements.index')->with('error', 'Advertisement not found');
|
return redirect()->route('web.advertisements.index')->with('error', 'Advertisement not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mengambil dan memetakan village_name dan district_name
|
// Mengambil dan memetakan village_name dan district_name
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class TourismController extends Controller
|
|||||||
$modelInstance = Tourism::find($id);
|
$modelInstance = Tourism::find($id);
|
||||||
// Pastikan model ditemukan
|
// Pastikan model ditemukan
|
||||||
if (!$modelInstance) {
|
if (!$modelInstance) {
|
||||||
return redirect()->route('tourisms.index') ->with('error', 'Pariwisata tidak ditemukan');
|
return redirect()->route('web-tourisms.index') ->with('error', 'Pariwisata tidak ditemukan');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mengambil dan memetakan village_name dan district_name
|
// Mengambil dan memetakan village_name dan district_name
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class UmkmController extends Controller
|
|||||||
$modelInstance = Umkm::find($id);
|
$modelInstance = Umkm::find($id);
|
||||||
// Pastikan model ditemukan
|
// Pastikan model ditemukan
|
||||||
if (!$modelInstance) {
|
if (!$modelInstance) {
|
||||||
return redirect()->route('umkm.index')->with('error', 'Umkm not found');
|
return redirect()->route('web-umkm.index')->with('error', 'Umkm not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mengambil dan memetakan village_name dan district_name
|
// Mengambil dan memetakan village_name dan district_name
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class PbgTaskController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(string $id)
|
public function show(string $id)
|
||||||
{
|
{
|
||||||
$data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana'])->findOrFail($id);
|
$data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana', 'taskAssignments'])->findOrFail($id);
|
||||||
return view("pbg_task.show", compact("data"));
|
return view("pbg_task.show", compact("data"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ class SyncronizeController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function syncPbgTask(){
|
public function syncPbgTask(){
|
||||||
$res = $this->service_simbg->syncTaskList();
|
$res = $this->service_simbg->syncTaskPBG();
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncronizeTask(Request $request){
|
public function syncronizeTask(Request $request){
|
||||||
$res = $this->service_simbg->syncTaskList();
|
$res = $this->service_simbg->syncTaskPBG();
|
||||||
return redirect()->back()->with('success', 'Processing completed successfully');
|
return redirect()->back()->with('success', 'Processing completed successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ class SyncronizeController extends Controller
|
|||||||
|
|
||||||
public function syncIndexIntegration(Request $request, $uuid){
|
public function syncIndexIntegration(Request $request, $uuid){
|
||||||
$token = $request->get('token');
|
$token = $request->get('token');
|
||||||
$res = $this->service_simbg->syncIndexIntegration($uuid, $token);
|
$res = $this->service_simbg->syncIndexIntegration($uuid);
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,4 +42,9 @@ class SyncronizeController extends Controller
|
|||||||
$res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token);
|
$res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token);
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function syncTaskAssignments($uuid){
|
||||||
|
$res = $this->service_simbg->syncTaskAssignments($uuid);
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
app/Http/Resources/BigdataResumeResource.php
Normal file
51
app/Http/Resources/BigdataResumeResource.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class BigdataResumeResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'import_datasource_id' => $this->import_datasource_id,
|
||||||
|
'potention_count' => (int) $this->potention_count,
|
||||||
|
'potention_sum' => number_format((float) $this->potention_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'non_verified_count' => (int) $this->non_verified_count,
|
||||||
|
'non_verified_sum' => number_format((float) $this->non_verified_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'verified_count' => (int) $this->verified_count,
|
||||||
|
'verified_sum' => number_format((float) $this->verified_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'business_count' => (int) $this->business_count,
|
||||||
|
'business_sum' => number_format((float) $this->business_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'non_business_count' => (int) $this->non_business_count,
|
||||||
|
'non_business_sum' => number_format((float) $this->non_business_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'spatial_count' => (int) $this->spatial_count,
|
||||||
|
'spatial_sum' => number_format((float) $this->spatial_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'issuance_realization_pbg_count' => (int) $this->issuance_realization_pbg_count,
|
||||||
|
'issuance_realization_pbg_sum' => number_format((float) $this->issuance_realization_pbg_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'waiting_click_dpmptsp_count' => (int) $this->waiting_click_dpmptsp_count,
|
||||||
|
'waiting_click_dpmptsp_sum' => number_format((float) $this->waiting_click_dpmptsp_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'process_in_technical_office_count' => (int) $this->process_in_technical_office_count,
|
||||||
|
'process_in_technical_office_sum' => number_format((float) $this->process_in_technical_office_sum, 2, ',', '.'),
|
||||||
|
|
||||||
|
'year' => $this->year,
|
||||||
|
'created_at' => $this->created_at->toDateTimeString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,37 +3,105 @@
|
|||||||
namespace App\Imports;
|
namespace App\Imports;
|
||||||
|
|
||||||
use App\Models\BusinessOrIndustry;
|
use App\Models\BusinessOrIndustry;
|
||||||
use Maatwebsite\Excel\Concerns\ToModel;
|
|
||||||
use Maatwebsite\Excel\Concerns\ToCollection;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class BusinessIndustriesImport implements ToCollection
|
class BusinessIndustriesImport implements ToCollection, WithMultipleSheets, WithChunkReading, WithBatchInserts, ShouldQueue, WithHeadingRow
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param array $row
|
* @param Collection $collection
|
||||||
*
|
|
||||||
* @return \Illuminate\Database\Eloquent\Model|null
|
|
||||||
*/
|
*/
|
||||||
public function collection(Collection $rows)
|
public function collection(Collection $collection)
|
||||||
{
|
{
|
||||||
foreach ($rows->skip(1) as $row){
|
try{
|
||||||
$clean_nop = preg_replace('/[^A-Za-z0-9]/', '', $row[2]);
|
$batchData = [];
|
||||||
if (!BusinessOrIndustry::where('nop', $clean_nop)->exists()) {
|
$batchSize = 1000;
|
||||||
BusinessOrIndustry::create([
|
|
||||||
'nama_kecamatan' => $row[0],
|
foreach ($collection as $row){
|
||||||
'nama_kelurahan' => $row[1],
|
if(!isset($row['nop']) || empty($row['nop'])){
|
||||||
'nop' => $clean_nop, // Store cleaned 'nop'
|
continue;
|
||||||
'nama_wajib_pajak' => $row[3],
|
}
|
||||||
'alamat_wajib_pajak' => $row[4],
|
|
||||||
'alamat_objek_pajak' => $row[5],
|
|
||||||
'luas_bumi' => $row[6],
|
$clean_nop = preg_replace('/[^A-Za-z0-9]/', '', $row['nop']);
|
||||||
'luas_bangunan' => $row[7],
|
|
||||||
'njop_bumi' => $row[8],
|
$batchData[] = [
|
||||||
'njop_bangunan' => $row[9],
|
'nama_kecamatan' => $row['nama_kecamatan'],
|
||||||
'ketetapan' => $row[10],
|
'nama_kelurahan' => $row['nama_kelurahan'],
|
||||||
'tahun_pajak' => $row[11],
|
'nop' => $clean_nop,
|
||||||
|
'nama_wajib_pajak' => $row['nama_wajib_pajak'],
|
||||||
|
'alamat_wajib_pajak' => $row['alamat_wajib_pajak'],
|
||||||
|
'alamat_objek_pajak' => $row['alamat_objek_pajak'],
|
||||||
|
'luas_bumi' => $row['luas_bumi'],
|
||||||
|
'luas_bangunan' => $row['luas_bangunan'],
|
||||||
|
'njop_bumi' => $row['njop_bumi'],
|
||||||
|
'njop_bangunan' => $row['njop_bangunan'],
|
||||||
|
'ketetapan' => $row['ketetapan'],
|
||||||
|
'tahun_pajak' => $row['tahun_pajak'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if(count($batchData) >= $batchSize){
|
||||||
|
BusinessOrIndustry::upsert($batchData, ['nop'], [
|
||||||
|
'nama_kecamatan',
|
||||||
|
'nama_kelurahan',
|
||||||
|
'nama_wajib_pajak',
|
||||||
|
'alamat_wajib_pajak',
|
||||||
|
'alamat_objek_pajak',
|
||||||
|
'luas_bumi',
|
||||||
|
'luas_bangunan',
|
||||||
|
'njop_bumi',
|
||||||
|
'njop_bangunan',
|
||||||
|
'ketetapan',
|
||||||
|
'tahun_pajak',
|
||||||
|
]);
|
||||||
|
$batchData = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!empty($batchData)){
|
||||||
|
BusinessOrIndustry::upsert($batchData, ['nop'], [
|
||||||
|
'nama_kecamatan',
|
||||||
|
'nama_kelurahan',
|
||||||
|
'nama_wajib_pajak',
|
||||||
|
'alamat_wajib_pajak',
|
||||||
|
'alamat_objek_pajak',
|
||||||
|
'luas_bumi',
|
||||||
|
'luas_bangunan',
|
||||||
|
'njop_bumi',
|
||||||
|
'njop_bangunan',
|
||||||
|
'ketetapan',
|
||||||
|
'tahun_pajak',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}catch(\Exception $exception){
|
||||||
|
Log::error('Error while importing Business Industries data:', ['error' => $exception->getMessage()]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sheets(): array {
|
||||||
|
return [
|
||||||
|
0 => $this
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headingRow(): int
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function chunkSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
|||||||
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class CustomersImport implements ToCollection, WithMultipleSheets
|
class CustomersImport implements ToCollection, WithMultipleSheets, WithChunkReading, WithBatchInserts, ShouldQueue, WithHeadingRow
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Collection $collection
|
* @param Collection $collection
|
||||||
@@ -20,23 +21,51 @@ class CustomersImport implements ToCollection, WithMultipleSheets
|
|||||||
public function collection(Collection $collection)
|
public function collection(Collection $collection)
|
||||||
{
|
{
|
||||||
$batchData = [];
|
$batchData = [];
|
||||||
|
$batchSize = 1000;
|
||||||
|
|
||||||
foreach ($collection->skip(1) as $row) {
|
foreach ($collection as $row) {
|
||||||
if (!isset($row[0]) || empty($row[0])) {
|
if (!isset($row['nomor_pelanggan']) || empty($row['nomor_pelanggan'])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$latitude = filter_var($row[4], FILTER_VALIDATE_FLOAT) ? bcadd($row[4], '0', 18) : null;
|
$latitude = '0';
|
||||||
$longitude = filter_var($row[5], FILTER_VALIDATE_FLOAT) ? bcadd($row[5], '0', 18) : null;
|
$longitude = '0';
|
||||||
|
|
||||||
|
if (isset($row['latkor']) && !empty(trim($row['latkor']))) {
|
||||||
|
$latitude = str_replace(',', '.', trim($row['latkor']));
|
||||||
|
if (is_numeric($latitude)) {
|
||||||
|
$latitude = bcadd($latitude, '0', 18);
|
||||||
|
} else {
|
||||||
|
$latitude = '0';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$latitude = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($row['lonkor']) && !empty(trim($row['lonkor']))) {
|
||||||
|
$longitude = str_replace(',', '.', trim($row['lonkor']));
|
||||||
|
if (is_numeric($longitude)) {
|
||||||
|
$longitude = bcadd($longitude, '0', 18);
|
||||||
|
} else {
|
||||||
|
$longitude = '0';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$longitude = '0';
|
||||||
|
}
|
||||||
|
|
||||||
$batchData[] = [
|
$batchData[] = [
|
||||||
'nomor_pelanggan' => $row[0],
|
'nomor_pelanggan' => $row['nomor_pelanggan'] ?? '',
|
||||||
'kota_pelayanan' => $row[1],
|
'kota_pelayanan' => $row['kota_pelayanan'] ?? '',
|
||||||
'nama' => $row[2],
|
'nama' => $row['nama'] ?? '',
|
||||||
'alamat' => $row[3],
|
'alamat' => $row['alamat'] ?? '',
|
||||||
'latitude' => $latitude,
|
'latitude' => $latitude,
|
||||||
'longitude' => $longitude,
|
'longitude' => $longitude,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (count($batchData) >= $batchSize) {
|
||||||
|
Customer::upsert($batchData, ['nomor_pelanggan'], ['kota_pelayanan', 'nama', 'alamat', 'latitude', 'longitude']);
|
||||||
|
$batchData = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($batchData)) {
|
if (!empty($batchData)) {
|
||||||
@@ -44,9 +73,20 @@ class CustomersImport implements ToCollection, WithMultipleSheets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function sheets(): array {
|
public function sheets(): array {
|
||||||
return [
|
return [
|
||||||
0 => $this
|
0 => $this
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function chunkSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
app/Jobs/SyncronizeSIMBG.php
Normal file
28
app/Jobs/SyncronizeSIMBG.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use App\Services\GoogleSheetService;
|
||||||
|
use App\Services\ServiceSIMBG;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class SyncronizeSIMBG implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public $tries = 1;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$serviceSIMBG = app(ServiceSIMBG::class);
|
||||||
|
$serviceSIMBG->syncTaskPBG();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,15 @@ class BigdataResume extends Model
|
|||||||
'business_sum',
|
'business_sum',
|
||||||
'non_business_count',
|
'non_business_count',
|
||||||
'non_business_sum',
|
'non_business_sum',
|
||||||
|
'spatial_count',
|
||||||
|
'spatial_sum',
|
||||||
|
'year',
|
||||||
|
'waiting_click_dpmptsp_count',
|
||||||
|
'waiting_click_dpmptsp_sum',
|
||||||
|
'issuance_realization_pbg_count',
|
||||||
|
'issuance_realization_pbg_sum',
|
||||||
|
'process_in_technical_office_count',
|
||||||
|
'process_in_technical_office_sum',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function importDatasource()
|
public function importDatasource()
|
||||||
@@ -28,88 +37,84 @@ class BigdataResume extends Model
|
|||||||
return $this->belongsTo(ImportDatasource::class, 'import_datasource_id');
|
return $this->belongsTo(ImportDatasource::class, 'import_datasource_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function generateResumeData($import_datasource_id){
|
public static function generateResumeData($import_datasource_id, $year, $data_setting){
|
||||||
$query_verified = once( function () {
|
$stats = PbgTask::with(['googleSheet', 'pbg_task_retributions'])
|
||||||
return DB::table('pbg_task AS pt')
|
->leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
|
||||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
->leftJoin('pbg_task_google_sheet as ptgs', 'pbg_task.registration_number', '=', 'ptgs.no_registrasi')
|
||||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
->when($year !== 'all', function ($query) use ($year) {
|
||||||
->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) = ?', [strtolower(trim('Selesai Verifikasi'))])
|
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||||
->selectRaw('COUNT(pt.id) AS total_data,
|
})
|
||||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution')
|
->selectRaw("
|
||||||
|
COUNT(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) = 'selesai verifikasi' THEN 1 END) AS verified_count,
|
||||||
|
SUM(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) = 'selesai verifikasi' THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS verified_total,
|
||||||
|
|
||||||
|
COUNT(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL THEN 1 END) AS non_verified_count,
|
||||||
|
SUM(CASE WHEN LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_verified_total,
|
||||||
|
|
||||||
|
COUNT(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||||
|
AND LOWER(TRIM(pbg_task.function_type)) = 'sebagai tempat usaha' THEN 1 END) AS business_count,
|
||||||
|
SUM(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||||
|
AND LOWER(TRIM(pbg_task.function_type)) = 'sebagai tempat usaha' THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS business_total,
|
||||||
|
|
||||||
|
COUNT(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||||
|
AND (LOWER(TRIM(pbg_task.function_type)) != 'sebagai tempat usaha' OR pbg_task.function_type IS NULL) THEN 1 END) AS non_business_count,
|
||||||
|
SUM(CASE WHEN (LOWER(TRIM(ptgs.status_verifikasi)) != 'selesai verifikasi' OR ptgs.status_verifikasi IS NULL)
|
||||||
|
AND (LOWER(TRIM(pbg_task.function_type)) != 'sebagai tempat usaha' OR pbg_task.function_type IS NULL) THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_business_total
|
||||||
|
")
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
|
// Assign Results
|
||||||
|
$verified_count = $stats->verified_count ?? 0;
|
||||||
|
$verified_total = $stats->verified_total ?? 0;
|
||||||
|
$non_verified_count = $stats->non_verified_count ?? 0;
|
||||||
|
$non_verified_total = $stats->non_verified_total ?? 0;
|
||||||
|
$business_count = $stats->business_count ?? 0;
|
||||||
|
$business_total = $stats->business_total ?? 0;
|
||||||
|
$non_business_count = $stats->non_business_count ?? 0;
|
||||||
|
$non_business_total = $stats->non_business_total ?? 0;
|
||||||
|
|
||||||
|
$query_potention = once(function () use ($year) {
|
||||||
|
$query = PbgTask::leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
|
||||||
|
->selectRaw('COUNT(DISTINCT pbg_task.id) as task_count, SUM(ptr.nilai_retribusi_bangunan) as total_retribution');
|
||||||
|
|
||||||
|
if ($year !== 'all') {
|
||||||
|
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->first();
|
||||||
});
|
});
|
||||||
|
|
||||||
$verified_count = $query_verified->total_data ?? 0;
|
|
||||||
$verified_total = $query_verified->total_retribution ?? 0;
|
|
||||||
|
|
||||||
$query_business = once(function () {
|
|
||||||
return DB::table('pbg_task AS pt')
|
|
||||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
|
||||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
|
||||||
->where(function ($query) {
|
|
||||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
|
||||||
->orWhereNull('ptgs.status_verifikasi');
|
|
||||||
})
|
|
||||||
->where(function ($query) {
|
|
||||||
$query->whereRaw('LOWER(TRIM(pt.function_type)) = ?', [strtolower(trim('Sebagai Tempat Usaha'))]);
|
|
||||||
})
|
|
||||||
->selectRaw('COUNT(pt.id) AS total_data,
|
|
||||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution')
|
|
||||||
->first();
|
|
||||||
});
|
|
||||||
|
|
||||||
$business_count = $query_business->total_data ?? 0;
|
|
||||||
$business_total = $query_business->total_retribution ?? 0;
|
|
||||||
|
|
||||||
$query_non_business = once( function () {
|
|
||||||
return DB::table('pbg_task AS pt')
|
|
||||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
|
||||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid') // Join ke pbg_task_retributions
|
|
||||||
->where(function ($query) {
|
|
||||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
|
||||||
->orWhereNull('ptgs.status_verifikasi'); // Include NULL values
|
|
||||||
})
|
|
||||||
->where(function ($query) {
|
|
||||||
$query->whereRaw('LOWER(TRIM(pt.function_type)) != ?', [strtolower(trim('Sebagai Tempat Usaha'))])
|
|
||||||
->orWhereNull('pt.function_type'); // Include NULL values
|
|
||||||
})
|
|
||||||
->selectRaw('COUNT(pt.id) AS total_data,
|
|
||||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution') // Menambahkan SUM dari pbg_task_retributions
|
|
||||||
->first();
|
|
||||||
});
|
|
||||||
$non_business_count = $query_non_business->total_data ?? 0;
|
|
||||||
$non_business_total = $query_non_business->total_retribution ?? 0;
|
|
||||||
|
|
||||||
$query_non_verified = once(function () {
|
|
||||||
return DB::table('pbg_task AS pt')
|
|
||||||
->leftJoin('pbg_task_google_sheet AS ptgs', 'pt.registration_number', '=', 'ptgs.no_registrasi')
|
|
||||||
->leftJoin('pbg_task_retributions AS ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid') // Join tabel pbg_task_retributions
|
|
||||||
->where(function ($query) {
|
|
||||||
$query->whereRaw('LOWER(TRIM(ptgs.status_verifikasi)) != ?', [strtolower(trim('Selesai Verifikasi'))])
|
|
||||||
->orWhereNull('ptgs.status_verifikasi'); // Include NULL values
|
|
||||||
})
|
|
||||||
->selectRaw('COUNT(pt.id) AS total_data,
|
|
||||||
SUM(ptr.nilai_retribusi_bangunan) AS total_retribution') // Menambahkan SUM dari pbg_task_retributions
|
|
||||||
->first();
|
|
||||||
});
|
|
||||||
|
|
||||||
$non_verified_count = $query_non_verified->total_data ?? 0;
|
|
||||||
$non_verified_total = $query_non_verified->total_retribution ?? 0;
|
|
||||||
|
|
||||||
$query_potention = once( function () {
|
|
||||||
return DB::table('pbg_task as pt')
|
|
||||||
->leftJoin('pbg_task_retributions as ptr', 'pt.uuid', '=', 'ptr.pbg_task_uid')
|
|
||||||
->select(
|
|
||||||
DB::raw('COUNT(DISTINCT pt.id) as task_count'),
|
|
||||||
DB::raw('SUM(ptr.nilai_retribusi_bangunan) as total_retribution')
|
|
||||||
)
|
|
||||||
->first();
|
|
||||||
});
|
|
||||||
$potention_count = $query_potention->task_count ?? 0;
|
$potention_count = $query_potention->task_count ?? 0;
|
||||||
$potention_total = $query_potention->total_retribution ?? 0;
|
$potention_total = $query_potention->total_retribution ?? 0;
|
||||||
|
|
||||||
|
$query_spatial_plannings = once(function () use ($year) {
|
||||||
|
$query = PbgTask::leftJoin('spatial_plannings as sp', 'pbg_task.document_number', '=', 'sp.number')
|
||||||
|
->leftJoin('pbg_task_retributions as ptr', 'ptr.pbg_task_uid', '=', 'pbg_task.uuid')
|
||||||
|
->selectRaw('
|
||||||
|
CASE
|
||||||
|
WHEN COUNT(DISTINCT sp.id) > 0 THEN COUNT(DISTINCT sp.id)
|
||||||
|
ELSE (SELECT COUNT(*) FROM spatial_plannings)
|
||||||
|
END as task_count,
|
||||||
|
SUM(CASE WHEN sp.id IS NOT NULL AND ptr.id IS NOT NULL THEN ptr.nilai_retribusi_bangunan ELSE 0 END) as total_retribution
|
||||||
|
');
|
||||||
|
|
||||||
|
if ($year !== 'all') {
|
||||||
|
$query->whereYear('pbg_task.task_created_at', (int) $year);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query->first();
|
||||||
|
});
|
||||||
|
|
||||||
|
$spatial_planning_count = $query_spatial_plannings->task_count ?? 0;
|
||||||
|
$spatial_planning_total = $query_spatial_plannings->total_retribution;
|
||||||
|
|
||||||
|
$potention_count -= $spatial_planning_count;
|
||||||
|
$potention_total -= $spatial_planning_total;
|
||||||
|
|
||||||
return self::create([
|
return self::create([
|
||||||
'import_datasource_id' => $import_datasource_id,
|
'import_datasource_id' => $import_datasource_id,
|
||||||
|
'spatial_count' => $spatial_planning_count,
|
||||||
|
'spatial_sum' => $spatial_planning_total ?? 0.00,
|
||||||
'potention_count' => $potention_count ?? 0,
|
'potention_count' => $potention_count ?? 0,
|
||||||
'potention_sum' => $potention_total ?? 0.00,
|
'potention_sum' => $potention_total ?? 0.00,
|
||||||
'non_verified_count' => $non_verified_count ?? 0,
|
'non_verified_count' => $non_verified_count ?? 0,
|
||||||
@@ -120,6 +125,13 @@ class BigdataResume extends Model
|
|||||||
'business_sum' => $business_total ?? 0.00,
|
'business_sum' => $business_total ?? 0.00,
|
||||||
'non_business_count' => $non_business_count ?? 0,
|
'non_business_count' => $non_business_count ?? 0,
|
||||||
'non_business_sum' => $non_business_total ?? 0.00,
|
'non_business_sum' => $non_business_total ?? 0.00,
|
||||||
|
'year' => $year,
|
||||||
|
'waiting_click_dpmptsp_count' => $data_setting['MENUNGGU_KLIK_DPMPTSP_COUNT'] ?? 0,
|
||||||
|
'waiting_click_dpmptsp_sum' => $data_setting['MENUNGGU_KLIK_DPMPTSP_SUM'] ?? 0.00,
|
||||||
|
'issuance_realization_pbg_count' => $data_setting['REALISASI_TERBIT_PBG_COUNT'] ?? 0,
|
||||||
|
'issuance_realization_pbg_sum' => $data_setting['REALISASI_TERBIT_PBG_SUM'] ?? 0.00,
|
||||||
|
'process_in_technical_office_count' => $data_setting['PROSES_DINAS_TEKNIS_COUNT'] ?? 0,
|
||||||
|
'process_in_technical_office_sum' => $data_setting['PROSES_DINAS_TEKNIS_SUM'] ??0.00,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,4 +37,13 @@ class PbgTask extends Model
|
|||||||
public function pbg_task_index_integrations(){
|
public function pbg_task_index_integrations(){
|
||||||
return $this->hasOne(PbgTaskIndexIntegrations::class, 'pbg_task_uid', 'uuid');
|
return $this->hasOne(PbgTaskIndexIntegrations::class, 'pbg_task_uid', 'uuid');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function googleSheet(){
|
||||||
|
return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function taskAssignments()
|
||||||
|
{
|
||||||
|
return $this->hasMany(TaskAssignment::class, 'pbg_task_uid', 'uuid');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
app/Models/TaskAssignment.php
Normal file
28
app/Models/TaskAssignment.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
class TaskAssignment extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id', 'name', 'username', 'email', 'phone_number', 'role',
|
||||||
|
'role_name', 'is_active', 'file', 'expertise', 'experience',
|
||||||
|
'is_verif', 'uid', 'status', 'status_name', 'note', 'pbg_task_uid'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
'is_verif' => 'boolean',
|
||||||
|
'file' => 'array', // JSON field casting
|
||||||
|
];
|
||||||
|
|
||||||
|
public function pbgTask()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(PbgTask::class, 'pbg_task_uid', 'uuid');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ use Illuminate\Support\Facades\Blade;
|
|||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use App\Services\ServiceSIMBG;
|
||||||
|
use App\Services\GoogleSheetService;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -17,7 +19,12 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
//
|
$this->app->singleton(GoogleSheetService::class, function () {
|
||||||
|
return new GoogleSheetService();
|
||||||
|
});
|
||||||
|
$this->app->singleton(ServiceSIMBG::class, function ($app) {
|
||||||
|
return new ServiceSIMBG($app->make(GoogleSheetService::class));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class GoogleSheetService
|
|||||||
protected $client;
|
protected $client;
|
||||||
protected $service;
|
protected $service;
|
||||||
protected $spreadsheetID;
|
protected $spreadsheetID;
|
||||||
|
protected $service_sheets;
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->client = new Google_Client();
|
$this->client = new Google_Client();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
|||||||
|
|
||||||
use OpenAI;
|
use OpenAI;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class OpenAIService
|
class OpenAIService
|
||||||
{
|
{
|
||||||
@@ -11,54 +12,11 @@ class OpenAIService
|
|||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
// $this->client = OpenAI::client(env('OPENAI_API_KEY'));
|
||||||
$this->client = OpenAI::client(env('OPENAI_API_KEY'));
|
$this->client = OpenAI::client(env('OPENAI_API_KEY'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateGeneralText($prompt, $mainContent)
|
public function generateQueryBasedMainContent($prompt, $mainContent, $chatHistory)
|
||||||
{
|
|
||||||
$response = $this->client->chat()->create([
|
|
||||||
'model' => 'gpt-4o-mini',
|
|
||||||
'messages' => [
|
|
||||||
[
|
|
||||||
'role' => 'system',
|
|
||||||
'content' => "You are an expert assistant. Your task is to generate a concise response based on the provided prompt and main content.
|
|
||||||
|
|
||||||
Guidelines:
|
|
||||||
- Summarize the key points in exactly 5 bullet points.
|
|
||||||
- Ensure the response is clear and relevant to the prompt.
|
|
||||||
- Use simple and professional language."
|
|
||||||
],
|
|
||||||
['role' => 'user', 'content' => "Prompt: $prompt \nMain Content: $mainContent"],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateClassifyMainContent($prompt, $mainContent)
|
|
||||||
{
|
|
||||||
$response = $this->client->chat()->create([
|
|
||||||
'model' => 'gpt-4o-mini',
|
|
||||||
'messages' => [
|
|
||||||
[
|
|
||||||
'role' => 'system',
|
|
||||||
'content' => "You are an expert assistant in classifying questions based on whether their answers must be retrieved from a database or can be explained generally.
|
|
||||||
Your task is to return one of the following two labels:
|
|
||||||
- \"DATABASE\" → If the question requires specific data that can only be obtained from a database.
|
|
||||||
- \"GENERAL\" → If the question can be answered without accessing a database.
|
|
||||||
|
|
||||||
Consider the following context: \"$mainContent\"
|
|
||||||
|
|
||||||
Respond with only one of the labels: \"DATABASE\" or \"GENERAL\"."
|
|
||||||
],
|
|
||||||
['role' => 'user', 'content' => $prompt],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateQueryBasedMainContent($prompt, $mainContent)
|
|
||||||
{
|
{
|
||||||
// Load file JSON
|
// Load file JSON
|
||||||
$jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path
|
$jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path
|
||||||
@@ -72,18 +30,60 @@ class OpenAIService
|
|||||||
// Ambil template berdasarkan kategori
|
// Ambil template berdasarkan kategori
|
||||||
$promptTemplate = $jsonData[$mainContent]['prompt'];
|
$promptTemplate = $jsonData[$mainContent]['prompt'];
|
||||||
|
|
||||||
|
// Menyusun pesan untuk OpenAI
|
||||||
|
$messages = [
|
||||||
|
['role' => 'system', 'content' => $promptTemplate],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Menambahkan chat history sebagai konteks
|
||||||
|
foreach ($chatHistory as $chat) {
|
||||||
|
if (isset($chat['user'])) {
|
||||||
|
$messages[] = ['role' => 'user', 'content' => $chat['user']];
|
||||||
|
}
|
||||||
|
if (isset($chat['rawBotResponse'])) {
|
||||||
|
$messages[] = ['role' => 'assistant', 'content' => $chat['rawBotResponse']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tambahkan prompt terbaru user
|
||||||
|
$messages[] = ['role' => 'user', 'content' => $prompt];
|
||||||
|
|
||||||
|
// Kirim request ke OpenAI API
|
||||||
$response = $this->client->chat()->create([
|
$response = $this->client->chat()->create([
|
||||||
'model' => 'gpt-4o-mini',
|
'model' => 'gpt-4o-mini',
|
||||||
'messages' => [
|
'messages' => $messages,
|
||||||
['role' => 'system', 'content' => $promptTemplate],
|
|
||||||
['role' => 'user', 'content' => $prompt],
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function generateQueryBasedMainContent($prompt, $mainContent, $chatHistory)
|
||||||
|
// {
|
||||||
|
// // Load file JSON
|
||||||
|
// $jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path
|
||||||
|
// $jsonData = json_decode(file_get_contents($jsonPath), true);
|
||||||
|
|
||||||
|
// // Periksa apakah kategori ada dalam JSON
|
||||||
|
// if (!isset($jsonData[$mainContent])) {
|
||||||
|
// return "Template prompt tidak ditemukan.";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Ambil template berdasarkan kategori
|
||||||
|
// $promptTemplate = $jsonData[$mainContent]['prompt'];
|
||||||
|
|
||||||
|
// $response = $this->client->chat()->create([
|
||||||
|
// 'model' => 'gpt-4o-mini',
|
||||||
|
// 'messages' => [
|
||||||
|
// ['role' => 'system', 'content' => $promptTemplate],
|
||||||
|
// ['role' => 'user', 'content' => $prompt],
|
||||||
|
// ],
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
public function validateSyntaxQuery($queryResponse)
|
public function validateSyntaxQuery($queryResponse)
|
||||||
{
|
{
|
||||||
$response = $this->client->chat()->create([
|
$response = $this->client->chat()->create([
|
||||||
@@ -154,4 +154,123 @@ class OpenAIService
|
|||||||
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function classifyMainGenerateText($prompt) {
|
||||||
|
$response = $this->client->chat()->create([
|
||||||
|
'model' => 'gpt-4o-mini',
|
||||||
|
'messages' => [
|
||||||
|
[
|
||||||
|
'role' => 'system',
|
||||||
|
'content' => "You are an assistant that classifies text into one of the following categories:
|
||||||
|
- reklame (ads or product/service promotions)
|
||||||
|
- business_or_industries (business or industries in general)
|
||||||
|
- customers (customers, consumers, or service users)
|
||||||
|
- pbg (tasks related to Building Approval)
|
||||||
|
- retribusi (retributions related to PBG)
|
||||||
|
- spatial_plannings (spatial planning)
|
||||||
|
- tourisms (tourism and tourist destinations)
|
||||||
|
- umkms (Micro, Small, and Medium Enterprises)
|
||||||
|
|
||||||
|
Respond with only one of the categories above without any additional explanation."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'role' => 'user',
|
||||||
|
'content' => "Classify the following text:\n\n" . $prompt
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createMainQuery($classify, $prompt, $chatHistory)
|
||||||
|
{
|
||||||
|
// Load file JSON
|
||||||
|
$jsonPath = public_path('templates/table_config.json');
|
||||||
|
$jsonConfig = json_decode(file_get_contents($jsonPath), true);
|
||||||
|
|
||||||
|
// Pastikan kategori tersedia dalam konfigurasi
|
||||||
|
if (!isset($jsonConfig[$classify])) {
|
||||||
|
return "Error: Kategori tidak ditemukan dalam konfigurasi.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ambil nama tabel dan kolom
|
||||||
|
$tableName = $jsonConfig[$classify]['table_name'];
|
||||||
|
$columns = implode(', ', $jsonConfig[$classify]['list_column']);
|
||||||
|
|
||||||
|
// Konversi chatHistory ke dalam format messages
|
||||||
|
$messages = [
|
||||||
|
[
|
||||||
|
'role' => 'system',
|
||||||
|
'content' => "You are an AI assistant that generates only valid MariaDB queries based on user requests.
|
||||||
|
Use the following table information to construct the SQL query:
|
||||||
|
|
||||||
|
- Table Name: $tableName
|
||||||
|
- Available Columns: $columns
|
||||||
|
|
||||||
|
Generate only the SQL query without any explanation or additional text.
|
||||||
|
The query should include `LIMIT 10` to restrict the results."
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Menambahkan chat history sebagai konteks
|
||||||
|
foreach ($chatHistory as $chat) {
|
||||||
|
if (isset($chat['user'])) {
|
||||||
|
$messages[] = ['role' => 'user', 'content' => $chat['user']];
|
||||||
|
}
|
||||||
|
if (isset($chat['rawBotResponse'])) {
|
||||||
|
$messages[] = ['role' => 'assistant', 'content' => $chat['rawBotResponse']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tambahkan prompt utama pengguna
|
||||||
|
$messages[] = ['role' => 'user', 'content' => $prompt];
|
||||||
|
|
||||||
|
// Kirim permintaan ke model AI
|
||||||
|
$response = $this->client->chat()->create([
|
||||||
|
'model' => 'gpt-4o-mini',
|
||||||
|
'messages' => $messages
|
||||||
|
]);
|
||||||
|
|
||||||
|
return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public function createMainQuery($classify, $prompt)
|
||||||
|
// {
|
||||||
|
// // Load file JSON
|
||||||
|
// $jsonPath = public_path('templates/table_config.json');
|
||||||
|
// $jsonConfig = json_decode(file_get_contents($jsonPath), true);
|
||||||
|
|
||||||
|
// // Pastikan kategori tersedia dalam konfigurasi
|
||||||
|
// if (!isset($jsonConfig[$classify])) {
|
||||||
|
// return "Error: Kategori tidak ditemukan dalam konfigurasi.";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Ambil nama tabel dan kolom
|
||||||
|
// $tableName = $jsonConfig[$classify]['table_name'];
|
||||||
|
// $columns = implode(', ', $jsonConfig[$classify]['list_column']);
|
||||||
|
|
||||||
|
// $response = $this->client->chat()->create([
|
||||||
|
// 'model' => 'gpt-4o-mini',
|
||||||
|
// 'messages' => [
|
||||||
|
// [
|
||||||
|
// 'role' => 'system',
|
||||||
|
// 'content' => "You are an AI assistant that generates only valid MariaDB queries based on user requests.
|
||||||
|
// Use the following table information to construct the SQL query:
|
||||||
|
|
||||||
|
// - Table Name: $tableName
|
||||||
|
// - Available Columns: $columns
|
||||||
|
|
||||||
|
// Generate only the SQL query without any explanation or additional text
|
||||||
|
// The query should include `LIMIT 10` to restrict the results."
|
||||||
|
// ],
|
||||||
|
// [
|
||||||
|
// 'role' => 'user',
|
||||||
|
// 'content' => $prompt
|
||||||
|
// ],
|
||||||
|
// ],
|
||||||
|
// ]);
|
||||||
|
|
||||||
|
// return trim($response['choices'][0]['message']['content'] ?? 'No response');
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,9 +51,25 @@ class ServiceClient
|
|||||||
|
|
||||||
$resultResponse = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
|
$resultResponse = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
|
||||||
return $this->resSuccess($resultResponse);
|
return $this->resSuccess($resultResponse);
|
||||||
|
} catch (\GuzzleHttp\Exception\ClientException $e) {
|
||||||
|
// Handle 4xx errors (e.g., 401 Unauthorized)
|
||||||
|
$responseBody = (string) $e->getResponse()->getBody();
|
||||||
|
$errorResponse = json_decode($responseBody, true);
|
||||||
|
|
||||||
|
if (isset($errorResponse['code']) && $errorResponse['code'] === 'token_not_valid') {
|
||||||
|
return $this->resError('Invalid token, please refresh your token.', $errorResponse, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resError('Client error from API', $errorResponse, $e->getResponse()->getStatusCode());
|
||||||
|
} catch (\GuzzleHttp\Exception\ServerException $e) {
|
||||||
|
// Handle 5xx errors (e.g., Internal Server Error)
|
||||||
|
return $this->resError('Server error from API', (string) $e->getResponse()->getBody(), 500);
|
||||||
|
} catch (\GuzzleHttp\Exception\RequestException $e) {
|
||||||
|
// Handle network errors (e.g., timeout, connection issues)
|
||||||
|
return $this->resError('Network error: ' . $e->getMessage(), null, 503);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
\Log::error('error from client service'. $e->getMessage());
|
// Handle unexpected errors
|
||||||
return $this->resError($e->getMessage());
|
return $this->resError('Unexpected error: ' . $e->getMessage(), null, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ use App\Models\ImportDatasource;
|
|||||||
use App\Models\PbgTaskIndexIntegrations;
|
use App\Models\PbgTaskIndexIntegrations;
|
||||||
use App\Models\PbgTaskPrasarana;
|
use App\Models\PbgTaskPrasarana;
|
||||||
use App\Models\PbgTaskRetributions;
|
use App\Models\PbgTaskRetributions;
|
||||||
|
use App\Models\TaskAssignment;
|
||||||
use Exception;
|
use Exception;
|
||||||
use App\Models\PbgTask;
|
use App\Models\PbgTask;
|
||||||
use App\Traits\GlobalApiResponse;
|
use App\Traits\GlobalApiResponse;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use App\Services\ServiceClient;
|
use App\Services\ServiceClient;
|
||||||
|
use App\Services\GoogleSheetService;
|
||||||
|
use App\Models\DataSetting;
|
||||||
|
use App\Models\PbgTaskGoogleSheet;
|
||||||
|
|
||||||
class ServiceSIMBG
|
class ServiceSIMBG
|
||||||
{
|
{
|
||||||
@@ -24,10 +28,11 @@ class ServiceSIMBG
|
|||||||
private $simbg_host;
|
private $simbg_host;
|
||||||
private $fetch_per_page;
|
private $fetch_per_page;
|
||||||
private $service_client;
|
private $service_client;
|
||||||
|
private $googleSheetService;
|
||||||
/**
|
/**
|
||||||
* Create a new class instance.
|
* Create a new class instance.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(GoogleSheetService $googleSheetService)
|
||||||
{
|
{
|
||||||
$settings = GlobalSetting::whereIn('key', [
|
$settings = GlobalSetting::whereIn('key', [
|
||||||
'SIMBG_EMAIL', 'SIMBG_PASSWORD', 'SIMBG_HOST', 'FETCH_PER_PAGE'
|
'SIMBG_EMAIL', 'SIMBG_PASSWORD', 'SIMBG_HOST', 'FETCH_PER_PAGE'
|
||||||
@@ -39,6 +44,7 @@ class ServiceSIMBG
|
|||||||
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? ""));
|
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? ""));
|
||||||
|
|
||||||
$this->service_client = new ServiceClient($this->simbg_host);
|
$this->service_client = new ServiceClient($this->simbg_host);
|
||||||
|
$this->googleSheetService = $googleSheetService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getToken(){
|
public function getToken(){
|
||||||
@@ -61,13 +67,20 @@ class ServiceSIMBG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncIndexIntegration($uuids, $token)
|
public function syncIndexIntegration($uuids)
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
if(empty($uuids)){
|
if(empty($uuids)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$initResToken = $this->getToken();
|
||||||
|
if (empty($initResToken->original['data']['token']['access'])) {
|
||||||
|
Log::error("API response indicates failure", ['token' => 'Failed to retrieve token']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$token = $initResToken->original['data']['token']['access'];
|
||||||
|
|
||||||
$integrations = [];
|
$integrations = [];
|
||||||
foreach($uuids as $uuid){
|
foreach($uuids as $uuid){
|
||||||
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
|
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
|
||||||
@@ -81,13 +94,13 @@ class ServiceSIMBG
|
|||||||
if (empty($res->original['success']) || !$res->original['success']) {
|
if (empty($res->original['success']) || !$res->original['success']) {
|
||||||
// Log error
|
// Log error
|
||||||
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $res->original['data']['data'] ?? null;
|
$data = $res->original['data']['data'] ?? null;
|
||||||
if (!$data) {
|
if (!$data) {
|
||||||
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$integrations[] = [
|
$integrations[] = [
|
||||||
@@ -105,19 +118,6 @@ class ServiceSIMBG
|
|||||||
PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan',
|
PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan',
|
||||||
'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']);
|
'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']);
|
||||||
|
|
||||||
// $resultData = PbgTaskIndexIntegrations::updateOrCreate(
|
|
||||||
// ['pbg_task_uid' => $uuid],
|
|
||||||
// [
|
|
||||||
// 'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null,
|
|
||||||
// 'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null,
|
|
||||||
// 'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null,
|
|
||||||
// 'indeks_parameter_ketinggian' => $data['indeks_parameter_ketinggian'] ?? null,
|
|
||||||
// 'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null,
|
|
||||||
// 'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
|
|
||||||
// 'total' => $data['total'] ?? null,
|
|
||||||
// ]
|
|
||||||
// );
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}catch (Exception $e){
|
}catch (Exception $e){
|
||||||
Log::error('error when sync index integration ', ['index integration'=> $e->getMessage()]);
|
Log::error('error when sync index integration ', ['index integration'=> $e->getMessage()]);
|
||||||
@@ -125,22 +125,184 @@ class ServiceSIMBG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncTaskList()
|
public function syncTaskPBG()
|
||||||
{
|
{
|
||||||
$initResToken = $this->getToken();
|
try {
|
||||||
|
Log::info("Processing google sheet sync");
|
||||||
$importDatasource = ImportDatasource::create([
|
$importDatasource = ImportDatasource::create([
|
||||||
'status' => ImportDatasourceStatus::Processing->value,
|
'status' => ImportDatasourceStatus::Processing->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// sync google sheet first
|
||||||
|
$totalRowCount = $this->googleSheetService->getLastRowByColumn("C");
|
||||||
|
$sheetData = $this->googleSheetService->getSheetDataCollection($totalRowCount);
|
||||||
|
$sheet_big_data = $this->googleSheetService->get_data_by_sheet();
|
||||||
|
$data_setting_result = []; // Initialize result storage
|
||||||
|
|
||||||
|
$found_section = null; // Track which section is found
|
||||||
|
|
||||||
|
foreach ($sheet_big_data as $row) {
|
||||||
|
// Check for section headers
|
||||||
|
if (in_array("•PROSES PENERBITAN:", $row)) {
|
||||||
|
$found_section = "MENUNGGU_KLIK_DPMPTSP";
|
||||||
|
} elseif (in_array("•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2024:", $row)) {
|
||||||
|
$found_section = "REALISASI_TERBIT_PBG";
|
||||||
|
} elseif (in_array("•TERPROSES DI DPUTR: belum selesai rekomtek'", $row)) {
|
||||||
|
$found_section = "PROSES_DINAS_TEKNIS";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a section is found and we reach "Grand Total", save the corresponding values
|
||||||
|
if ($found_section && isset($row[0]) && trim($row[0]) === "Grand Total") {
|
||||||
|
if ($found_section === "MENUNGGU_KLIK_DPMPTSP") {
|
||||||
|
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $this->convertToInteger($row[2]) ?? null;
|
||||||
|
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $this->convertToDecimal($row[3]) ?? null;
|
||||||
|
} elseif ($found_section === "REALISASI_TERBIT_PBG") {
|
||||||
|
$data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $this->convertToInteger($row[2]) ?? null;
|
||||||
|
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $this->convertToDecimal($row[4]) ?? null;
|
||||||
|
} elseif ($found_section === "PROSES_DINAS_TEKNIS") {
|
||||||
|
$data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $this->convertToInteger($row[2]) ?? null;
|
||||||
|
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $this->convertToDecimal($row[3]) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset section tracking after capturing "Grand Total"
|
||||||
|
$found_section = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info("data setting result", ['result' => $data_setting_result]);
|
||||||
|
|
||||||
|
foreach ($data_setting_result as $key => $value) {
|
||||||
|
DataSetting::updateOrInsert(
|
||||||
|
["key" => $key], // Find by key
|
||||||
|
["value" => $value] // Update or insert value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$mapToUpsert = [];
|
||||||
|
|
||||||
|
foreach($sheetData as $data){
|
||||||
|
$mapToUpsert[] =
|
||||||
|
[
|
||||||
|
'no_registrasi' => $data['no__registrasi'] ?? null,
|
||||||
|
'jenis_konsultasi' => $data['jenis_konsultasi'] ?? null,
|
||||||
|
'fungsi_bg' => $data['fungsi_bg'] ?? null,
|
||||||
|
'tgl_permohonan' => $this->convertToDate($data['tgl_permohonan']),
|
||||||
|
'status_verifikasi' => $data['status_verifikasi'] ?? null,
|
||||||
|
'status_permohonan' => $this->convertToDate($data['status_permohonan']),
|
||||||
|
'alamat_pemilik' => $data['alamat_pemilik'] ?? null,
|
||||||
|
'no_hp' => $data['no__hp'] ?? null,
|
||||||
|
'email' => $data['e_mail'] ?? null,
|
||||||
|
'tanggal_catatan' => $this->convertToDate($data['tanggal_catatan']),
|
||||||
|
'catatan_kekurangan_dokumen' => $data['catatan_kekurangan_dokumen'] ?? null,
|
||||||
|
'gambar' => $data['gambar'] ?? null,
|
||||||
|
'krk_kkpr' => $data['krk_kkpr'] ?? null,
|
||||||
|
'no_krk' => $data['no__krk'] ?? null,
|
||||||
|
'lh' => $data['lh'] ?? null,
|
||||||
|
'ska' => $data['ska'] ?? null,
|
||||||
|
'keterangan' => $data['keterangan'] ?? null,
|
||||||
|
'helpdesk' => $data['helpdesk'] ?? null,
|
||||||
|
'pj' => $data['pj'] ?? null,
|
||||||
|
'kepemilikan' => $data['kepemilikan'] ?? null,
|
||||||
|
'potensi_taru' => $data['potensi_taru'] ?? null,
|
||||||
|
'validasi_dinas' => $data['validasi_dinas'] ?? null,
|
||||||
|
'kategori_retribusi' => $data['kategori_retribusi'] ?? null,
|
||||||
|
'no_urut_ba_tpt' => $data['no__urut_ba_tpt__2024_0001_'] ?? null,
|
||||||
|
'tanggal_ba_tpt' => $this->convertToDate($data['tanggal_ba_tpt']),
|
||||||
|
'no_urut_ba_tpa' => $data['no__urut_ba_tpa'] ?? null,
|
||||||
|
'tanggal_ba_tpa' => $this->convertToDate($data['tanggal_ba_tpa']),
|
||||||
|
'no_urut_skrd' => $data['no__urut_skrd__2024_0001_'] ?? null,
|
||||||
|
'tanggal_skrd' => $this->convertToDate($data['tanggal_skrd']),
|
||||||
|
'ptsp' => $data['ptsp'] ?? null,
|
||||||
|
'selesai_terbit' => $data['selesai_terbit'] ?? null,
|
||||||
|
'tanggal_pembayaran' => $this->convertToDate($data['tanggal_pembayaran__yyyy_mm_dd_']),
|
||||||
|
'format_sts' => $data['format_sts'] ?? null,
|
||||||
|
'tahun_terbit' => (int) $data['tahun_terbit'] ?? null,
|
||||||
|
'tahun_berjalan' => (int) $data['tahun_berjalan'] ?? null,
|
||||||
|
'kelurahan' => $data['kelurahan'] ?? null,
|
||||||
|
'kecamatan' => $data['kecamatan'] ?? null,
|
||||||
|
'lb' => $this->convertToDecimal($data['lb']) ?? null,
|
||||||
|
'tb' => $this->convertToDecimal($data['tb']) ?? null,
|
||||||
|
'jlb' => (int) $data['jlb'] ?? null,
|
||||||
|
'unit' => (int) $data['unit'] ?? null,
|
||||||
|
'usulan_retribusi' => (int) $data['usulan_retribusi'] ?? null,
|
||||||
|
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_']) ?? null,
|
||||||
|
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_']) ?? null,
|
||||||
|
'denda' => $this->convertToDecimal($data['denda']) ?? null,
|
||||||
|
'latitude' => $data['latitude'] ?? null,
|
||||||
|
'longitude' => $data['longitude'] ?? null,
|
||||||
|
'nik_nib' => $data['nik_nib'] ?? null,
|
||||||
|
'dok_tanah' => $data['dok__tanah'] ?? null,
|
||||||
|
'temuan' => $data['temuan'] ?? null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$batchSize = 1000;
|
||||||
|
$chunks = array_chunk($mapToUpsert, $batchSize);
|
||||||
|
|
||||||
|
foreach($chunks as $chunk){
|
||||||
|
PbgTaskGoogleSheet::upsert($chunk, ["no_registrasi"],[
|
||||||
|
'jenis_konsultasi',
|
||||||
|
'nama_pemilik',
|
||||||
|
'lokasi_bg',
|
||||||
|
'fungsi_bg',
|
||||||
|
'nama_bangunan',
|
||||||
|
'tgl_permohonan',
|
||||||
|
'status_verifikasi',
|
||||||
|
'status_permohonan',
|
||||||
|
'alamat_pemilik',
|
||||||
|
'no_hp',
|
||||||
|
'email',
|
||||||
|
'tanggal_catatan',
|
||||||
|
'catatan_kekurangan_dokumen',
|
||||||
|
'gambar',
|
||||||
|
'krk_kkpr',
|
||||||
|
'no_krk',
|
||||||
|
'lh',
|
||||||
|
'ska',
|
||||||
|
'keterangan',
|
||||||
|
'helpdesk',
|
||||||
|
'pj',
|
||||||
|
'kepemilikan',
|
||||||
|
'potensi_taru',
|
||||||
|
'validasi_dinas',
|
||||||
|
'kategori_retribusi',
|
||||||
|
'no_urut_ba_tpt',
|
||||||
|
'tanggal_ba_tpt',
|
||||||
|
'no_urut_ba_tpa',
|
||||||
|
'tanggal_ba_tpa',
|
||||||
|
'no_urut_skrd',
|
||||||
|
'tanggal_skrd',
|
||||||
|
'ptsp',
|
||||||
|
'selesai_terbit',
|
||||||
|
'tanggal_pembayaran',
|
||||||
|
'format_sts',
|
||||||
|
'tahun_terbit',
|
||||||
|
'tahun_berjalan',
|
||||||
|
'kelurahan',
|
||||||
|
'kecamatan',
|
||||||
|
'lb',
|
||||||
|
'tb',
|
||||||
|
'jlb',
|
||||||
|
'unit',
|
||||||
|
'usulan_retribusi',
|
||||||
|
'nilai_retribusi_keseluruhan_simbg',
|
||||||
|
'nilai_retribusi_keseluruhan_pad',
|
||||||
|
'denda',
|
||||||
|
'latitude',
|
||||||
|
'longitude',
|
||||||
|
'nik_nib',
|
||||||
|
'dok_tanah',
|
||||||
|
'temuan',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$initResToken = $this->getToken();
|
||||||
if (empty($initResToken->original['data']['token']['access'])) {
|
if (empty($initResToken->original['data']['token']['access'])) {
|
||||||
$importDatasource->update([
|
$importDatasource->update([
|
||||||
'status' => ImportDatasourceStatus::Failed->value,
|
'status' => ImportDatasourceStatus::Failed->value,
|
||||||
'message' => 'Failed to retrieve token'
|
'response_body' => 'Failed to retrieve token'
|
||||||
]);
|
]);
|
||||||
return $this->resError("Failed to retrieve token");
|
return $this->resError("Failed to retrieve token");
|
||||||
}
|
}
|
||||||
|
|
||||||
$apiToken = $initResToken->original['data']['token']['access'];
|
$apiToken = $initResToken->original['data']['token']['access'];
|
||||||
$headers = ['Authorization' => "Bearer " . $apiToken];
|
$headers = ['Authorization' => "Bearer " . $apiToken];
|
||||||
|
|
||||||
@@ -151,40 +313,64 @@ class ServiceSIMBG
|
|||||||
if ($totalPage == 0) {
|
if ($totalPage == 0) {
|
||||||
$importDatasource->update([
|
$importDatasource->update([
|
||||||
'status' => ImportDatasourceStatus::Failed->value,
|
'status' => ImportDatasourceStatus::Failed->value,
|
||||||
'message' => 'Invalid response: no total_page'
|
'response_body' => 'Invalid response: no total_page'
|
||||||
]);
|
]);
|
||||||
return $this->resError("Invalid response from API");
|
return $this->resError("Invalid response from API");
|
||||||
}
|
}
|
||||||
|
|
||||||
$savedCount = $failedCount = 0;
|
$savedCount = $failedCount = 0;
|
||||||
|
|
||||||
|
Log::info("Fetching tasks", ['total page' => $totalPage]);
|
||||||
|
|
||||||
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
|
for ($currentPage = 1; $currentPage <= $totalPage; $currentPage++) {
|
||||||
|
try {
|
||||||
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
|
$pageUrl = "/api/pbg/v1/list/?page={$currentPage}&size={$this->fetch_per_page}&sort=ASC";
|
||||||
$getToken = $this->getToken();
|
|
||||||
Log::info("response index integration", ['currentPage' => $currentPage]);
|
Log::info("Fetching tasks", ['currentPage' => $currentPage]);
|
||||||
if (empty($getToken->original['data']['token']['access'])) {
|
$headers = [
|
||||||
$importDatasource->update([
|
'Authorization' => "Bearer " . $apiToken, // Update headers
|
||||||
'status' => ImportDatasourceStatus::Failed->value,
|
];
|
||||||
'message' => 'Failed to retrieve token'
|
|
||||||
]);
|
for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry)
|
||||||
|
|
||||||
|
$response = $this->service_client->get($pageUrl, $headers);
|
||||||
|
|
||||||
|
if ($response instanceof \Illuminate\Http\JsonResponse) {
|
||||||
|
$decodedResponse = json_decode($response->getContent(), true);
|
||||||
|
|
||||||
|
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
|
||||||
|
Log::warning("Token is invalid, refreshing token...");
|
||||||
|
|
||||||
|
// Regenerate token
|
||||||
|
$initResToken = $this->getToken();
|
||||||
|
|
||||||
|
// Check if new token is valid
|
||||||
|
if (!empty($initResToken->original['data']['token']['access'])) {
|
||||||
|
$new_token = $initResToken->original['data']['token']['access'];
|
||||||
|
|
||||||
|
// **Fix: Update headers before retrying**
|
||||||
|
$headers['Authorization'] = "Bearer " . $new_token;
|
||||||
|
|
||||||
|
Log::info("Token refreshed successfully, retrying API request...");
|
||||||
|
continue; // Retry with new token
|
||||||
|
} else {
|
||||||
|
Log::error("Failed to refresh token");
|
||||||
|
return $this->resError("Failed to refresh token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success case, break loop
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$token = $getToken->original['data']['token']['access'];
|
|
||||||
$headers = ['Authorization' => "Bearer " . $token];
|
|
||||||
$response = $this->service_client->get($pageUrl, $headers);
|
|
||||||
$tasks = $response->original['data']['data'] ?? [];
|
$tasks = $response->original['data']['data'] ?? [];
|
||||||
|
|
||||||
if (empty($tasks)) {
|
if (empty($tasks)) {
|
||||||
$importDatasource->update([
|
|
||||||
'status' => ImportDatasourceStatus::Failed->value,
|
|
||||||
'message' => 'No data found on page'
|
|
||||||
]);
|
|
||||||
Log::warning("No data found on page", ['page' => $currentPage]);
|
Log::warning("No data found on page", ['page' => $currentPage]);
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::info("executed page", ['page' => $currentPage, 'total' => $totalPage]);
|
|
||||||
|
|
||||||
$tasksCollective = [];
|
$tasksCollective = [];
|
||||||
foreach ($tasks as $item) {
|
foreach ($tasks as $item) {
|
||||||
try {
|
try {
|
||||||
@@ -211,25 +397,20 @@ class ServiceSIMBG
|
|||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// $this->syncIndexIntegration($item['uid'], $token);
|
$this->syncTaskDetailSubmit($item['uid'], $apiToken);
|
||||||
|
$this->syncTaskAssignments($item['uid']);
|
||||||
$this->syncTaskDetailSubmit($item['uid'], $token);
|
|
||||||
|
|
||||||
$savedCount++;
|
$savedCount++;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$failedCount++;
|
$failedCount++;
|
||||||
$importDatasource->update([
|
|
||||||
'status' => ImportDatasourceStatus::Failed->value,
|
|
||||||
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
|
||||||
]);
|
|
||||||
Log::error("Failed to process task", [
|
Log::error("Failed to process task", [
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'task' => $item,
|
'task' => $item,
|
||||||
]);
|
]);
|
||||||
break;
|
continue; // Skip failed task, continue processing the rest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($tasksCollective)) {
|
||||||
PbgTask::upsert($tasksCollective, ['uuid'], [
|
PbgTask::upsert($tasksCollective, ['uuid'], [
|
||||||
'name', 'owner_name', 'application_type', 'application_type_name', 'condition',
|
'name', 'owner_name', 'application_type', 'application_type_name', 'condition',
|
||||||
'registration_number', 'document_number', 'address', 'status', 'status_name',
|
'registration_number', 'document_number', 'address', 'status', 'status_name',
|
||||||
@@ -238,41 +419,90 @@ class ServiceSIMBG
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$uuids = array_column($tasksCollective, 'uuid');
|
$uuids = array_column($tasksCollective, 'uuid');
|
||||||
$this->syncIndexIntegration($uuids, $token);
|
$this->syncIndexIntegration($uuids);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error("Failed to process page", [
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
'page' => $currentPage,
|
||||||
|
]);
|
||||||
|
continue; // Skip the failed page and move to the next
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BigdataResume::generateResumeData($importDatasource->id, "all", $data_setting_result);
|
||||||
|
BigdataResume::generateResumeData($importDatasource->id, now()->year, $data_setting_result);
|
||||||
|
|
||||||
|
// Final update after processing all pages
|
||||||
$importDatasource->update([
|
$importDatasource->update([
|
||||||
'status' => ImportDatasourceStatus::Success->value,
|
'status' => ImportDatasourceStatus::Success->value,
|
||||||
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
'message' => "Successfully processed: $savedCount, Failed: $failedCount"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
BigdataResume::generateResumeData($importDatasource->id);
|
|
||||||
|
|
||||||
Log::info("syncTaskList completed", ['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
Log::info("syncTaskList completed", ['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||||
|
|
||||||
return $this->resSuccess(['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
return $this->resSuccess(['savedCount' => $savedCount, 'failedCount' => $failedCount]);
|
||||||
}
|
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error("syncTaskList failed", ['error' => $e->getMessage()]);
|
||||||
|
if (isset($importDatasource)) {
|
||||||
|
$importDatasource->update([
|
||||||
|
'status' => ImportDatasourceStatus::Failed->value,
|
||||||
|
'response_body' => 'Critical failure: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $this->resError("Critical failure occurred: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function syncTaskDetailSubmit($uuid, $token)
|
public function syncTaskDetailSubmit($uuid, $token)
|
||||||
{
|
{
|
||||||
try{
|
try{
|
||||||
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/";
|
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/submit/";
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
'Authorization' => "Bearer " . $token,
|
'Authorization' => "Bearer " . $token,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
for ($attempt = 0; $attempt < 2; $attempt++) { // Try twice (original + retry)
|
||||||
$res = $this->service_client->get($url, $headers);
|
$res = $this->service_client->get($url, $headers);
|
||||||
|
|
||||||
if (empty($res->original['success']) || !$res->original['success']) {
|
// Check if response is JsonResponse and decode it
|
||||||
// Log error
|
if ($res instanceof \Illuminate\Http\JsonResponse) {
|
||||||
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
|
$decodedResponse = json_decode($res->getContent(), true);
|
||||||
return false;
|
|
||||||
|
// Handle invalid token case
|
||||||
|
if (isset($decodedResponse['errors']['code']) && $decodedResponse['errors']['code'] === 'token_not_valid') {
|
||||||
|
Log::warning("Token is invalid, refreshing token...");
|
||||||
|
|
||||||
|
// Regenerate the token
|
||||||
|
$initResToken = $this->getToken();
|
||||||
|
|
||||||
|
// Check if the new token is valid
|
||||||
|
if (!empty($initResToken->original['data']['token']['access'])) {
|
||||||
|
$new_token = $initResToken->original['data']['token']['access'];
|
||||||
|
|
||||||
|
// **Fix: Update headers with the new token**
|
||||||
|
$headers['Authorization'] = "Bearer " . $new_token;
|
||||||
|
|
||||||
|
Log::info("Token refreshed successfully, retrying API request...");
|
||||||
|
continue; // Retry the request with the new token
|
||||||
|
} else {
|
||||||
|
Log::error("Failed to refresh token");
|
||||||
|
return $this->resError("Failed to refresh token");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $res->original['data']['data'] ?? [];
|
// If request succeeds, break out of retry loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure response is valid before accessing properties
|
||||||
|
$responseData = $res->original ?? [];
|
||||||
|
$data = $responseData['data']['data'] ?? [];
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
Log::error("No data returned from API", ['url' => $url, 'uuid' => $uuid]);
|
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid, 'response' => $responseData]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,4 +565,112 @@ class ServiceSIMBG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function syncTaskAssignments($uuid){
|
||||||
|
try{
|
||||||
|
$init_token = $this->getToken();
|
||||||
|
$token = $init_token->original['data']['token']['access'];
|
||||||
|
$url = "/api/pbg/v1/list-tim-penilai/". $uuid . "/?page=1&size=10";
|
||||||
|
$headers = [
|
||||||
|
'Authorization' => "Bearer " . $token,
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $this->service_client->get($url, $headers);
|
||||||
|
$datas = $response->original['data']['data'] ?? [];
|
||||||
|
if(empty($datas)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$task_assignments = [];
|
||||||
|
|
||||||
|
foreach ($datas as $data) {
|
||||||
|
$task_assignments[] = [
|
||||||
|
'pbg_task_uid' => $uuid, // Assuming this is a foreign key
|
||||||
|
'user_id' => $data['user_id'],
|
||||||
|
'name' => $data['name'],
|
||||||
|
'username' => $data['username'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'phone_number' => $data['phone_number'],
|
||||||
|
'role' => $data['role'],
|
||||||
|
'role_name' => $data['role_name'],
|
||||||
|
'is_active' => $data['is_active'],
|
||||||
|
'file' => json_encode($data['file']), // Store as JSON if it's an array
|
||||||
|
'expertise' => $data['expertise'],
|
||||||
|
'experience' => $data['experience'],
|
||||||
|
'is_verif' => $data['is_verif'],
|
||||||
|
'uid' => $data['uid'], // Unique identifier
|
||||||
|
'status' => $data['status'],
|
||||||
|
'status_name' => $data['status_name'],
|
||||||
|
'note' => $data['note'],
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
TaskAssignment::upsert(
|
||||||
|
$task_assignments, // Data to insert/update
|
||||||
|
['uid'], // Unique key for conflict resolution
|
||||||
|
['name', 'username', 'email', 'phone_number', 'role', 'role_name', 'is_active', 'file', 'expertise', 'experience', 'is_verif', 'status', 'status_name', 'note', 'updated_at']
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}catch(Exception $e){
|
||||||
|
Log::error("Failed to sync task assignments", ['error' => $e->getMessage()]);
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function convertToDecimal(?string $value): ?float
|
||||||
|
{
|
||||||
|
if (empty($value)) {
|
||||||
|
return null; // Return null if the input is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all non-numeric characters except comma and dot
|
||||||
|
$value = preg_replace('/[^0-9,\.]/', '', $value);
|
||||||
|
|
||||||
|
// If the number contains both dot (.) and comma (,)
|
||||||
|
if (strpos($value, '.') !== false && strpos($value, ',') !== false) {
|
||||||
|
$value = str_replace('.', '', $value); // Remove thousands separator
|
||||||
|
$value = str_replace(',', '.', $value); // Convert decimal separator to dot
|
||||||
|
}
|
||||||
|
// If only a dot is present (assumed as thousands separator)
|
||||||
|
elseif (strpos($value, '.') !== false) {
|
||||||
|
$value = str_replace('.', '', $value); // Remove all dots (treat as thousands separators)
|
||||||
|
}
|
||||||
|
// If only a comma is present (assumed as decimal separator)
|
||||||
|
elseif (strpos($value, ',') !== false) {
|
||||||
|
$value = str_replace(',', '.', $value); // Convert comma to dot (decimal separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the value is numeric before returning
|
||||||
|
return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function convertToInteger($value) {
|
||||||
|
// Check if the value is an empty string, and return null if true
|
||||||
|
if (trim($value) === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleaned = str_replace('.','', $value);
|
||||||
|
|
||||||
|
// Otherwise, cast to integer
|
||||||
|
return (int) $cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function convertToDate($dateString)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check if the string is empty
|
||||||
|
if (empty($dateString)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse the date string
|
||||||
|
$date = Carbon::parse($dateString);
|
||||||
|
|
||||||
|
// Return the Carbon instance
|
||||||
|
return $date->format('Y-m-d');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Return null if an error occurs during parsing
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
],
|
],
|
||||||
"dev": [
|
"dev": [
|
||||||
"Composer\\Config::disableProcessTimeout",
|
"Composer\\Config::disableProcessTimeout",
|
||||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
|
|||||||
@@ -123,5 +123,6 @@ return [
|
|||||||
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
'store' => env('APP_MAINTENANCE_STORE', 'database'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'api_url' => env('API_URL', 'http://localhost:8000')
|
'api_url' => env('API_URL', 'http://localhost:8000'),
|
||||||
|
'paginate_per_page' => 50
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -109,4 +109,10 @@ return [
|
|||||||
'table' => 'failed_jobs',
|
'table' => 'failed_jobs',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// set timeout queue
|
||||||
|
|
||||||
|
'worker' => [
|
||||||
|
'timeout' => 300
|
||||||
|
]
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||||
|
$table->integer('spatial_count')->default(0);
|
||||||
|
$table->decimal('spatial_sum', 20,2)->default(0);
|
||||||
|
$table->string('year');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('spatial_count');
|
||||||
|
$table->dropColumn('spatial_sum');
|
||||||
|
$table->dropColumn('year');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||||
|
$table->integer('waiting_click_dpmptsp_count')->default(0);
|
||||||
|
$table->decimal('waiting_click_dpmptsp_sum', 20,2)->default(0);
|
||||||
|
$table->integer('issuance_realization_pbg_count')->default(0);
|
||||||
|
$table->decimal('issuance_realization_pbg_sum', 20,2)->default(0);
|
||||||
|
$table->integer('process_in_technical_office_count')->default(0);
|
||||||
|
$table->decimal('process_in_technical_office_sum', 20,2)->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('bigdata_resumes', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('waiting_click_dpmptsp_count');
|
||||||
|
$table->dropColumn('waiting_click_dpmptsp_sum');
|
||||||
|
$table->dropColumn('issuance_realization_pbg_count');
|
||||||
|
$table->dropColumn('issuance_realization_pbg_sum');
|
||||||
|
$table->dropColumn('process_in_technical_office_count');
|
||||||
|
$table->dropColumn('process_in_technical_office_sum');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('task_assignments', function (Blueprint $table) {
|
||||||
|
$table->id(); // Auto-increment primary key
|
||||||
|
|
||||||
|
// Foreign key reference to pbg_tasks (uid column)
|
||||||
|
$table->string('pbg_task_uid');
|
||||||
|
$table->foreign('pbg_task_uid')->references('uuid')->on('pbg_task')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('user_id'); // Reference to users table
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('username')->unique();
|
||||||
|
$table->string('email')->unique();
|
||||||
|
$table->string('phone_number')->nullable();
|
||||||
|
$table->unsignedInteger('role'); // Assuming role is numeric
|
||||||
|
$table->string('role_name');
|
||||||
|
$table->boolean('is_active')->default(true);
|
||||||
|
$table->json('file')->nullable(); // Store as JSON if 'file' is an array
|
||||||
|
$table->string('expertise')->nullable();
|
||||||
|
$table->string('experience')->nullable();
|
||||||
|
$table->boolean('is_verif')->default(false);
|
||||||
|
$table->string('uid')->unique();
|
||||||
|
$table->unsignedTinyInteger('status')->default(0); // Assuming status is a small integer
|
||||||
|
$table->string('status_name')->nullable();
|
||||||
|
$table->text('note')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('task_assignments');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -71,9 +71,16 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
[
|
[
|
||||||
"name" => "Laporan",
|
"name" => "Laporan",
|
||||||
"url" => "/laporan",
|
"url" => "/laporan",
|
||||||
"icon" => "mingcute:task-line",
|
"icon" => "mingcute:report-line",
|
||||||
"parent_id" => null,
|
"parent_id" => null,
|
||||||
"sort_order" => 6,
|
"sort_order" => 6,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Neng Bedas",
|
||||||
|
"url" => "/chat",
|
||||||
|
"icon" => "mingcute:wechat-line",
|
||||||
|
"parent_id" => null,
|
||||||
|
"sort_order" => 7,
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -92,6 +99,7 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
$dataSettings = Menu::where('name', 'Data Settings')->first();
|
$dataSettings = Menu::where('name', 'Data Settings')->first();
|
||||||
$data = Menu::where('name', 'Data')->first();
|
$data = Menu::where('name', 'Data')->first();
|
||||||
$laporan = Menu::where('name', 'Laporan')->first();
|
$laporan = Menu::where('name', 'Laporan')->first();
|
||||||
|
$chat_bedas = Menu::where('name', 'Neng Bedas')->first();
|
||||||
|
|
||||||
// create children menu
|
// create children menu
|
||||||
$children_menus = [
|
$children_menus = [
|
||||||
@@ -167,7 +175,7 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"name" => "Reklame",
|
"name" => "Reklame",
|
||||||
"url" => "advertisements.index",
|
"url" => "web.advertisements.index",
|
||||||
"icon" => null,
|
"icon" => null,
|
||||||
"parent_id" => $data->id,
|
"parent_id" => $data->id,
|
||||||
"sort_order" => 2,
|
"sort_order" => 2,
|
||||||
@@ -181,21 +189,21 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"name" => "UMKM",
|
"name" => "UMKM",
|
||||||
"url" => "umkm.index",
|
"url" => "web-umkm.index",
|
||||||
"icon" => null,
|
"icon" => null,
|
||||||
"parent_id" => $data->id,
|
"parent_id" => $data->id,
|
||||||
"sort_order" => 4,
|
"sort_order" => 4,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"name" => "Pariwisata",
|
"name" => "Pariwisata",
|
||||||
"url" => "tourisms.index",
|
"url" => "web-tourisms.index",
|
||||||
"icon" => null,
|
"icon" => null,
|
||||||
"parent_id" => $data->id,
|
"parent_id" => $data->id,
|
||||||
"sort_order" => 5,
|
"sort_order" => 5,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"name" => "Tata Ruang",
|
"name" => "Tata Ruang",
|
||||||
"url" => "spatial-plannings.index",
|
"url" => "web-spatial-plannings.index",
|
||||||
"icon" => null,
|
"icon" => null,
|
||||||
"parent_id" => $data->id,
|
"parent_id" => $data->id,
|
||||||
"sort_order" => 6,
|
"sort_order" => 6,
|
||||||
@@ -209,11 +217,25 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"name" => "Lap Pariwisata",
|
"name" => "Lap Pariwisata",
|
||||||
"url" => "tourisms.index",
|
"url" => "tourisms-report.index",
|
||||||
"icon" => null,
|
"icon" => null,
|
||||||
"parent_id" => $laporan->id,
|
"parent_id" => $laporan->id,
|
||||||
"sort_order" => 1,
|
"sort_order" => 1,
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"name" => "Lap Pimpinan",
|
||||||
|
"url" => "bigdata-resumes",
|
||||||
|
"icon" => null,
|
||||||
|
"parent_id" => $laporan->id,
|
||||||
|
"sort_order" => 2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Chat",
|
||||||
|
"url" => "main-chatbot.index",
|
||||||
|
"icon" => null,
|
||||||
|
"parent_id" => $chat_bedas->id,
|
||||||
|
"sort_order" => 1,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($children_menus as $child_menu) {
|
foreach ($children_menus as $child_menu) {
|
||||||
@@ -237,6 +259,8 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
$spatial_plannings = Menu::where('name', 'Tata Ruang')->first();
|
$spatial_plannings = Menu::where('name', 'Tata Ruang')->first();
|
||||||
$pdam = Menu::where('name', 'PDAM')->first();
|
$pdam = Menu::where('name', 'PDAM')->first();
|
||||||
$peta = Menu::where('name', 'PETA')->first();
|
$peta = Menu::where('name', 'PETA')->first();
|
||||||
|
$bigdata_resume = Menu::where('name', 'Lap Pimpinan')->first();
|
||||||
|
$chatbot = Menu::where('name', 'Chat')->first();
|
||||||
|
|
||||||
// Superadmin gets all menus
|
// Superadmin gets all menus
|
||||||
$superadmin->menus()->sync([
|
$superadmin->menus()->sync([
|
||||||
@@ -247,6 +271,7 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
$dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
$dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
||||||
$data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
$data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
||||||
$laporan->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
$laporan->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
||||||
|
$chat_bedas->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
|
||||||
// children
|
// children
|
||||||
$dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
$dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
@@ -265,6 +290,8 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
$spatial_plannings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$spatial_plannings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
$pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
$peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
|
$bigdata_resume->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
|
$chatbot->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Admin gets limited menus
|
// Admin gets limited menus
|
||||||
@@ -279,6 +306,7 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
$dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
$data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
$data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Attach User to role super admin
|
// Attach User to role super admin
|
||||||
User::findOrFail(1)->roles()->sync([$superadmin->id]);
|
User::findOrFail(1)->roles()->sync([$superadmin->id]);
|
||||||
}
|
}
|
||||||
|
|||||||
21
database/view_query/v_advertisements.sql
Normal file
21
database/view_query/v_advertisements.sql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
CREATE VIEW v_advertisements AS
|
||||||
|
SELECT
|
||||||
|
a.no,
|
||||||
|
a.business_name,
|
||||||
|
a.npwpd,
|
||||||
|
a.advertisement_type,
|
||||||
|
a.advertisement_content,
|
||||||
|
a.business_address,
|
||||||
|
a.advertisement_location,
|
||||||
|
v.village_name AS village_name,
|
||||||
|
d.district_name AS district_name,
|
||||||
|
a.length,
|
||||||
|
a.width,
|
||||||
|
a.viewing_angle,
|
||||||
|
a.face,
|
||||||
|
a.area,
|
||||||
|
a.angle,
|
||||||
|
a.contact
|
||||||
|
FROM advertisements a
|
||||||
|
JOIN villages v ON a.village_code = v.village_code
|
||||||
|
JOIN districts d ON a.district_code = d.district_code;
|
||||||
8
database/view_query/v_tourism_base_kbli.sql
Normal file
8
database/view_query/v_tourism_base_kbli.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE VIEW v_tourisms_based_kbli AS
|
||||||
|
SELECT kbli_title, total_records
|
||||||
|
FROM (
|
||||||
|
SELECT kbli, kbli_title, COUNT(*) AS total_records
|
||||||
|
FROM tourisms
|
||||||
|
GROUP BY kbli, kbli_title
|
||||||
|
) AS subquery
|
||||||
|
ORDER BY total_records DESC;
|
||||||
29
database/view_query/v_tourisms.sql
Normal file
29
database/view_query/v_tourisms.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
CREATE VIEW v_tourisms AS
|
||||||
|
SELECT
|
||||||
|
t.project_id,
|
||||||
|
t.project_type_id,
|
||||||
|
t.nib,
|
||||||
|
t.business_name,
|
||||||
|
t.oss_publication_date,
|
||||||
|
t.investment_status_description,
|
||||||
|
t.business_form,
|
||||||
|
t.project_risk,
|
||||||
|
t.project_name,
|
||||||
|
t.business_scale,
|
||||||
|
t.business_address,
|
||||||
|
v.village_name as village_name,
|
||||||
|
d.district_name as district_name,
|
||||||
|
t.longitude,
|
||||||
|
t.latitude,
|
||||||
|
t.project_submission_date,
|
||||||
|
t.kbli_title,
|
||||||
|
t.supervisory_sector,
|
||||||
|
t.user_name,
|
||||||
|
t.email,
|
||||||
|
t.contact,
|
||||||
|
t.land_area_in_m2,
|
||||||
|
t.investment_amount,
|
||||||
|
t.tki
|
||||||
|
FROM tourisms t
|
||||||
|
JOIN villages v on t.village_code = v.village_code
|
||||||
|
JOIN districts d on t.district_code = d.district_code;
|
||||||
28
database/view_query/v_umkms.sql
Normal file
28
database/view_query/v_umkms.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
CREATE VIEW v_umkms AS
|
||||||
|
SELECT
|
||||||
|
u.business_address,
|
||||||
|
u.business_contact,
|
||||||
|
u.business_desc,
|
||||||
|
bf.business_form,
|
||||||
|
u.business_id_number,
|
||||||
|
u.business_name,
|
||||||
|
bs.business_scale,
|
||||||
|
u.business_type,
|
||||||
|
u.created_at,
|
||||||
|
d.district_name,
|
||||||
|
u.land_area,
|
||||||
|
u.number_of_employee,
|
||||||
|
u.owner_address,
|
||||||
|
u.owner_contact,
|
||||||
|
u.owner_id,
|
||||||
|
u.owner_name,
|
||||||
|
ps.permit_status,
|
||||||
|
u.revenue,
|
||||||
|
u.updated_at,
|
||||||
|
v.village_name
|
||||||
|
FROM umkms u
|
||||||
|
JOIN business_form bf on u.business_form_id = bf.id
|
||||||
|
JOIN permit_status ps on u.permit_status_id = ps.id
|
||||||
|
JOIn business_scale bs on u.business_scale_id = bs.id
|
||||||
|
JOIN villages v on u.village_code = v.village_code
|
||||||
|
JOIN districts d on u.district_code = v.district_code;
|
||||||
34
deploy.sh
Executable file
34
deploy.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
GIT_BRANCH="dev"
|
||||||
|
PHP_VERSION="php8.3"
|
||||||
|
|
||||||
|
echo "🚀 Starting deployment..."
|
||||||
|
php artisan down
|
||||||
|
|
||||||
|
echo "📥 Pulling latest changes from Git..."
|
||||||
|
git fetch origin $GIT_BRANCH
|
||||||
|
git reset --hard origin/$GIT_BRANCH
|
||||||
|
git pull origin $GIT_BRANCH
|
||||||
|
|
||||||
|
echo "⚡ Installing NPM dependencies and building assets..."
|
||||||
|
npm ci --no-audit --no-fund
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
echo "📦 Installing composer dependencies..."
|
||||||
|
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction --optimize-autoloader
|
||||||
|
|
||||||
|
echo "🗄️ Running migrations..."
|
||||||
|
php artisan migrate --force
|
||||||
|
|
||||||
|
echo "⚡ Optimizing application..."
|
||||||
|
php artisan optimize:clear
|
||||||
|
|
||||||
|
echo "🔄 Restarting PHP service..."
|
||||||
|
systemctl restart $PHP_VERSION-fpm
|
||||||
|
|
||||||
|
echo "🔁 Restarting Supervisor queue workers..."
|
||||||
|
supervisorctl stop all
|
||||||
|
supervisorctl reload
|
||||||
|
supervisorctl start all
|
||||||
|
|
||||||
|
php artisan up
|
||||||
|
echo "🚀 Deployment completed successfully!"
|
||||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "sibedas-pbg",
|
"name": "sibedas-pbg-web",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"gridjs": "^5.1.0",
|
"gridjs": "^5.1.0",
|
||||||
"iconify-icon": "^2.1.0",
|
"iconify-icon": "^2.1.0",
|
||||||
"jsvectormap": "^1.5.1",
|
"jsvectormap": "^1.5.1",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-waves": "^0.7.6",
|
"node-waves": "^0.7.6",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
@@ -1467,6 +1468,12 @@
|
|||||||
"vite": "^5.0.0 || ^6.0.0"
|
"vite": "^5.0.0 || ^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/leaflet": {
|
||||||
|
"version": "1.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||||
|
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"gridjs": "^5.1.0",
|
"gridjs": "^5.1.0",
|
||||||
"iconify-icon": "^2.1.0",
|
"iconify-icon": "^2.1.0",
|
||||||
"jsvectormap": "^1.5.1",
|
"jsvectormap": "^1.5.1",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-waves": "^0.7.6",
|
"node-waves": "^0.7.6",
|
||||||
"quill": "^1.3.7",
|
"quill": "^1.3.7",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ use Illuminate\Http\Request;
|
|||||||
define('LARAVEL_START', microtime(true));
|
define('LARAVEL_START', microtime(true));
|
||||||
|
|
||||||
ini_set('max_execution_time',14400);
|
ini_set('max_execution_time',14400);
|
||||||
|
ini_set('memory_limit', '2G');
|
||||||
|
|
||||||
// Determine if the application is in maintenance mode...
|
// Determine if the application is in maintenance mode...
|
||||||
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"RETRIBUTION": {
|
"RETRIBUTION": {
|
||||||
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
|
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results."
|
||||||
},
|
},
|
||||||
"DOCUMENT VALIDATION": {
|
"DOCUMENT VALIDATION": {
|
||||||
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
|
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results."
|
||||||
},
|
},
|
||||||
"DATA SUMMARY": {
|
"DATA SUMMARY": {
|
||||||
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
|
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation.\n\n The query should include `LIMIT 5` to restrict the results."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
169
public/templates/table_config.json
Normal file
169
public/templates/table_config.json
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
{
|
||||||
|
"reklame": {
|
||||||
|
"table_name": "v_advertisements",
|
||||||
|
"list_column": [
|
||||||
|
"no",
|
||||||
|
"business_name",
|
||||||
|
"npwpd",
|
||||||
|
"advertisement_type",
|
||||||
|
"advertisement_content",
|
||||||
|
"business_address",
|
||||||
|
"advertisement_location",
|
||||||
|
"village_name",
|
||||||
|
"district_name",
|
||||||
|
"length",
|
||||||
|
"width",
|
||||||
|
"viewing_angle",
|
||||||
|
"face",
|
||||||
|
"area",
|
||||||
|
"angle",
|
||||||
|
"contact"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"business_or_industries": {
|
||||||
|
"table_name": "business_or_industries",
|
||||||
|
"list_column": [
|
||||||
|
"nama_kecamatan",
|
||||||
|
"nama_kelurahan",
|
||||||
|
"nop",
|
||||||
|
"nama_wajib_pajak",
|
||||||
|
"alamat_wajib_pajak",
|
||||||
|
"alamat_objek_pajak",
|
||||||
|
"luas_bumi",
|
||||||
|
"luas_bangunan",
|
||||||
|
"njop_bumi",
|
||||||
|
"njop_bangunan",
|
||||||
|
"ketetapan",
|
||||||
|
"tahun_pajak",
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"customers": {
|
||||||
|
"table_name": "customers",
|
||||||
|
"list_column": [
|
||||||
|
"nomor_pelanggan",
|
||||||
|
"kota_pelayanan",
|
||||||
|
"nama",
|
||||||
|
"alamat",
|
||||||
|
"latitude",
|
||||||
|
"longitude",
|
||||||
|
"created_at",
|
||||||
|
"updated_at"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pbg": {
|
||||||
|
"table_name": "pbg_task",
|
||||||
|
"list_column": [
|
||||||
|
"uuid",
|
||||||
|
"name",
|
||||||
|
"owner_name",
|
||||||
|
"application_type",
|
||||||
|
"application_type_name",
|
||||||
|
"condition",
|
||||||
|
"registration_number",
|
||||||
|
"document_number",
|
||||||
|
"address",
|
||||||
|
"status_name",
|
||||||
|
"slf_status_name",
|
||||||
|
"function_type",
|
||||||
|
"consultation_type",
|
||||||
|
"due_date",
|
||||||
|
"land_certificate_phase",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"task_created_at"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"retribusi": {
|
||||||
|
"table_name": "v_pbg_task_with_retributions",
|
||||||
|
"list_column": [
|
||||||
|
"uuid",
|
||||||
|
"name",
|
||||||
|
"owner_name",
|
||||||
|
"application_type",
|
||||||
|
"application_type_name",
|
||||||
|
"condition",
|
||||||
|
"registration_number",
|
||||||
|
"document_number",
|
||||||
|
"address",
|
||||||
|
"status_name",
|
||||||
|
"slf_status_name",
|
||||||
|
"consultation_type",
|
||||||
|
"due_date",
|
||||||
|
"land_certificate_phase",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"task_created_at",
|
||||||
|
"nilai_retribusi_bangunan"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"spatial_plannings": {
|
||||||
|
"table_name": "spatial_plannings",
|
||||||
|
"list_column": [
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"name",
|
||||||
|
"kbli",
|
||||||
|
"activities",
|
||||||
|
"area",
|
||||||
|
"location",
|
||||||
|
"number",
|
||||||
|
"date"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tourisms": {
|
||||||
|
"table_name": "v_tourisms",
|
||||||
|
"list_column": [
|
||||||
|
"project_id",
|
||||||
|
"project_type_id",
|
||||||
|
"nib",
|
||||||
|
"business_name",
|
||||||
|
"oss_publication_date",
|
||||||
|
"investment_status_description",
|
||||||
|
"business_form",
|
||||||
|
"project_risk",
|
||||||
|
"project_name",
|
||||||
|
"business_scale",
|
||||||
|
"business_address",
|
||||||
|
"village_name",
|
||||||
|
"district_name",
|
||||||
|
"longitude",
|
||||||
|
"latitude",
|
||||||
|
"project_submission_date",
|
||||||
|
"kbli_title",
|
||||||
|
"supervisory_sector",
|
||||||
|
"user_name",
|
||||||
|
"email",
|
||||||
|
"contact",
|
||||||
|
"land_area_in_m2",
|
||||||
|
"investment_amount",
|
||||||
|
"tki"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"umkms": {
|
||||||
|
"table_name": "v_umkms",
|
||||||
|
"list_column": [
|
||||||
|
"business_address",
|
||||||
|
"business_contact",
|
||||||
|
"business_desc",
|
||||||
|
"business_form",
|
||||||
|
"business_id_number",
|
||||||
|
"business_name",
|
||||||
|
"business_scale",
|
||||||
|
"business_type",
|
||||||
|
"created_at",
|
||||||
|
"district_name",
|
||||||
|
"land_area",
|
||||||
|
"number_of_employee",
|
||||||
|
"owner_address",
|
||||||
|
"owner_contact",
|
||||||
|
"owner_id",
|
||||||
|
"owner_name",
|
||||||
|
"permit_status",
|
||||||
|
"revenue",
|
||||||
|
"updated_at",
|
||||||
|
"village_name"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
172
resources/js/bigdata-resumes/index.js
Normal file
172
resources/js/bigdata-resumes/index.js
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||||
|
import gridjs from "gridjs/dist/gridjs.umd.js";
|
||||||
|
import "gridjs/dist/gridjs.umd.js";
|
||||||
|
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
class BigdataResume {
|
||||||
|
constructor() {
|
||||||
|
this.toastMessage = document.getElementById("toast-message");
|
||||||
|
this.toastElement = document.getElementById("toastNotification");
|
||||||
|
this.toast = new bootstrap.Toast(this.toastElement);
|
||||||
|
this.table = null;
|
||||||
|
|
||||||
|
// Initialize functions
|
||||||
|
this.initTableDataSettings();
|
||||||
|
// this.initEvents();
|
||||||
|
}
|
||||||
|
initEvents() {
|
||||||
|
document.body.addEventListener("click", async (event) => {
|
||||||
|
const deleteButton = event.target.closest(
|
||||||
|
".btn-delete-data-settings"
|
||||||
|
);
|
||||||
|
if (deleteButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this.handleDelete(deleteButton);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initTableDataSettings() {
|
||||||
|
let tableContainer = document.getElementById("table-bigdata-resumes");
|
||||||
|
this.table = new Grid({
|
||||||
|
columns: [
|
||||||
|
{ name: "ID" },
|
||||||
|
{ name: "Jumlah Potensi" },
|
||||||
|
{ name: "Total Potensi" },
|
||||||
|
{ name: "Jumlah Berkas Belum Terverifikasi" },
|
||||||
|
{ name: "Total Berkas Belum Terverifikasi" },
|
||||||
|
{ name: "Jumlah Berkas Terverifikasi" },
|
||||||
|
{ name: "Total Berkas Terverifikasi" },
|
||||||
|
{ name: "Jumlah Usaha" },
|
||||||
|
{ name: "Total Usaha" },
|
||||||
|
{ name: "Jumlah Non Usaha" },
|
||||||
|
{ name: "Total Non Usaha" },
|
||||||
|
{ name: "Jumlah Tata Ruang" },
|
||||||
|
{ name: "Total Tata Ruang" },
|
||||||
|
{ name: "Jumlah Menunggu Klik DPMPTSP" },
|
||||||
|
{ name: "Total Menunggu Klik DPMPTSP" },
|
||||||
|
{ name: "Jumlah Realisasi Terbit PBG" },
|
||||||
|
{ name: "Total Realisasi Terbit PBG" },
|
||||||
|
{ name: "Jumlah Proses Dinas Teknis" },
|
||||||
|
{ name: "Total Proses Dinas Teknis" },
|
||||||
|
{
|
||||||
|
name: "Created",
|
||||||
|
attributes: { style: "width: 200px; white-space: nowrap;" }, // Set width dynamically
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
limit: 50,
|
||||||
|
server: {
|
||||||
|
url: (prev, page) =>
|
||||||
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
page + 1
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sort: true,
|
||||||
|
// search: {
|
||||||
|
// server: {
|
||||||
|
// url: (prev, keyword) => `${prev}?search=${keyword}`,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
server: {
|
||||||
|
url: `${GlobalConfig.apiHost}/api/bigdata-report`,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
then: (data) => {
|
||||||
|
return data.data.map((item) => [
|
||||||
|
item.id,
|
||||||
|
item.potention_count,
|
||||||
|
addThousandSeparators(item.potention_sum),
|
||||||
|
item.non_verified_count,
|
||||||
|
addThousandSeparators(item.non_verified_sum),
|
||||||
|
item.verified_count,
|
||||||
|
addThousandSeparators(item.verified_sum),
|
||||||
|
item.business_count,
|
||||||
|
addThousandSeparators(item.business_sum),
|
||||||
|
item.non_business_count,
|
||||||
|
addThousandSeparators(item.non_business_sum),
|
||||||
|
item.spatial_count,
|
||||||
|
addThousandSeparators(item.spatial_sum),
|
||||||
|
item.waiting_click_dpmptsp_count,
|
||||||
|
addThousandSeparators(item.waiting_click_dpmptsp_sum),
|
||||||
|
item.issuance_realization_pbg_count,
|
||||||
|
addThousandSeparators(
|
||||||
|
item.issuance_realization_pbg_sum
|
||||||
|
),
|
||||||
|
item.process_in_technical_office_count,
|
||||||
|
addThousandSeparators(
|
||||||
|
item.process_in_technical_office_sum
|
||||||
|
),
|
||||||
|
moment(item.created_at).format("YYYY-MM-DD H:mm:ss"),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
total: (data) => data.total,
|
||||||
|
},
|
||||||
|
width: "auto",
|
||||||
|
}).render(tableContainer);
|
||||||
|
}
|
||||||
|
handleSearch() {}
|
||||||
|
async handleDelete(deleteButton) {
|
||||||
|
const id = deleteButton.getAttribute("data-id");
|
||||||
|
|
||||||
|
const result = await Swal.fire({
|
||||||
|
title: "Are you sure?",
|
||||||
|
text: "You won't be able to revert this!",
|
||||||
|
icon: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#3085d6",
|
||||||
|
cancelButtonColor: "#d33",
|
||||||
|
confirmButtonText: "Yes, delete it!",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
try {
|
||||||
|
let response = await fetch(
|
||||||
|
`${GlobalConfig.apiHost}/api/data-settings/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
let result = await response.json();
|
||||||
|
this.toastMessage.innerText =
|
||||||
|
result.message || "Deleted successfully!";
|
||||||
|
this.toast.show();
|
||||||
|
|
||||||
|
// Refresh Grid.js table
|
||||||
|
if (typeof this.table !== "undefined") {
|
||||||
|
this.table.updateConfig({}).forceRender();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let error = await response.json();
|
||||||
|
console.error("Delete failed:", error);
|
||||||
|
this.toastMessage.innerText =
|
||||||
|
error.message || "Delete failed!";
|
||||||
|
this.toast.show();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting item:", error);
|
||||||
|
this.toastMessage.innerText = "An error occurred!";
|
||||||
|
this.toast.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", function (e) {
|
||||||
|
new BigdataResume();
|
||||||
|
});
|
||||||
@@ -5,9 +5,9 @@ Dropzone.autoDiscover = false;
|
|||||||
var previewTemplate,
|
var previewTemplate,
|
||||||
dropzone,
|
dropzone,
|
||||||
dropzonePreviewNode = document.querySelector("#dropzone-preview-list");
|
dropzonePreviewNode = document.querySelector("#dropzone-preview-list");
|
||||||
console.log(previewTemplate);
|
|
||||||
console.log(dropzone);
|
const uploadButton = document.getElementById("btnUploadBusinessIndustry");
|
||||||
console.log(dropzonePreviewNode);
|
const spinner = document.getElementById("spinner");
|
||||||
|
|
||||||
const toastNotification = document.getElementById("toastNotification");
|
const toastNotification = document.getElementById("toastNotification");
|
||||||
const toast = new bootstrap.Toast(toastNotification);
|
const toast = new bootstrap.Toast(toastNotification);
|
||||||
@@ -29,97 +29,50 @@ const toast = new bootstrap.Toast(toastNotification);
|
|||||||
.getAttribute("content")}`,
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
init: function () {
|
init: function () {
|
||||||
// Listen for the success event
|
|
||||||
this.on("success", function (file, response) {
|
this.on("success", function (file, response) {
|
||||||
console.log("File successfully uploaded:", file);
|
|
||||||
console.log("API Response:", response);
|
|
||||||
|
|
||||||
// Show success toast
|
|
||||||
document.getElementById("toast-message").innerText =
|
document.getElementById("toast-message").innerText =
|
||||||
response.message;
|
response.message;
|
||||||
toast.show();
|
toast.show();
|
||||||
document.getElementById("submit-upload").innerHTML =
|
|
||||||
"Upload Files";
|
|
||||||
// Tunggu sebentar lalu reload halaman
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/business-industries";
|
window.location.href = "/data/business-industries";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
// Listen for the error event
|
|
||||||
this.on("error", function (file, errorMessage) {
|
this.on("error", function (file, errorMessage) {
|
||||||
console.error("Error uploading file:", file);
|
console.error("Error uploading file:", file);
|
||||||
console.error("Error message:", errorMessage);
|
console.error("Error message:", errorMessage);
|
||||||
// Handle the error response
|
|
||||||
|
|
||||||
// Show error toast
|
|
||||||
document.getElementById("toast-message").innerText =
|
document.getElementById("toast-message").innerText =
|
||||||
errorMessage.message;
|
errorMessage.message;
|
||||||
toast.show();
|
toast.show();
|
||||||
document.getElementById("submit-upload").innerHTML =
|
uploadButton.disabled = false;
|
||||||
"Upload Files";
|
spinner.classList.add("d-none");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Add event listener to control the submission manually
|
// Add event listener to control the submission manually
|
||||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
document
|
||||||
|
.querySelector("#btnUploadBusinessIndustry")
|
||||||
|
.addEventListener("click", function () {
|
||||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
console.log("Dropzonefiles", dropzone.files);
|
|
||||||
|
|
||||||
this.innerHTML =
|
|
||||||
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
|
||||||
|
|
||||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
|
||||||
if (dropzone.files.length > 0) {
|
if (dropzone.files.length > 0) {
|
||||||
formData.append("file", dropzone.files[0]);
|
formData.append("file", dropzone.files[0]);
|
||||||
console.log("ini adalah form data on submit", ...formData);
|
|
||||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||||
|
uploadButton.disabled = true;
|
||||||
|
spinner.classList.remove("d-none");
|
||||||
} else {
|
} else {
|
||||||
// Show error toast when no file is selected
|
|
||||||
document.getElementById("toast-message").innerText =
|
document.getElementById("toast-message").innerText =
|
||||||
"Please add a file first.";
|
"Please add a file first.";
|
||||||
toast.show();
|
toast.show();
|
||||||
|
uploadButton.disabled = false;
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
spinner.classList.add("d-none");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional: Listen for the 'addedfile' event to log or control file add behavior
|
dropzone.on("addedfile", function (file) {});
|
||||||
dropzone.on("addedfile", function (file) {
|
|
||||||
console.log("File ditambahkan:", file);
|
|
||||||
console.log("Nama File:", file.name);
|
|
||||||
console.log("Tipe File:", file.type);
|
|
||||||
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
|
|
||||||
});
|
|
||||||
|
|
||||||
dropzone.on("complete", function (file) {
|
dropzone.on("complete", function (file) {
|
||||||
dropzone.removeFile(file);
|
dropzone.removeFile(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to show toast
|
|
||||||
// function showToast(iconClass, iconColor, message) {
|
|
||||||
// const toastElement = document.getElementById("toastUploadAdvertisement");
|
|
||||||
// const toastBody = toastElement.querySelector(".toast-body");
|
|
||||||
// const toastHeader = toastElement.querySelector(".toast-header");
|
|
||||||
|
|
||||||
// // Remove existing icon (if any) before adding the new one
|
|
||||||
// const existingIcon = toastHeader.querySelector(".bx");
|
|
||||||
// if (existingIcon) {
|
|
||||||
// toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Add the new icon to the toast header
|
|
||||||
// const icon = document.createElement("i");
|
|
||||||
// icon.classList.add("bx", iconClass);
|
|
||||||
// icon.style.fontSize = "25px";
|
|
||||||
// icon.style.color = iconColor;
|
|
||||||
// toastHeader.querySelector(".auth-logo").appendChild(icon);
|
|
||||||
|
|
||||||
// // Set the toast message
|
|
||||||
// toastBody.textContent = message;
|
|
||||||
|
|
||||||
// // Show the toast
|
|
||||||
// const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
|
||||||
// toast.show();
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class BusinessIndustries {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
|||||||
155
resources/js/chatbot-pimpinan/index.js
Normal file
155
resources/js/chatbot-pimpinan/index.js
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import GlobalConfig from "../global-config.js";
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const timeElements = document.querySelectorAll(".sending-message-time p");
|
||||||
|
|
||||||
|
timeElements.forEach((element) => {
|
||||||
|
element.textContent = getCurrentTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
const textarea = document.getElementById("user-message");
|
||||||
|
const sendButton = document.getElementById("send");
|
||||||
|
const conversationArea = document.querySelector(".row.flex-grow");
|
||||||
|
const chatHistory = [];
|
||||||
|
|
||||||
|
// Fungsi untuk mengirim pesan
|
||||||
|
async function sendMessage() {
|
||||||
|
const userText = textarea.value.trim();
|
||||||
|
if (userText !== "") {
|
||||||
|
// Kosongkan textarea setelah mengirim
|
||||||
|
textarea.value = "";
|
||||||
|
|
||||||
|
// Tambahkan pesan user ke UI
|
||||||
|
addMessage(userText, "user");
|
||||||
|
|
||||||
|
// Tambahkan pesan bot sementara dengan "Loading..."
|
||||||
|
const botMessageElement = addMessage('<div class="bot-message-text">...</div>', "bot");
|
||||||
|
|
||||||
|
const messageTextContainer = botMessageElement.querySelector(".bot-message-text");
|
||||||
|
if (messageTextContainer) {
|
||||||
|
messageTextContainer.innerHTML = '<div class="loader ms-3"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panggil API untuk mendapatkan respons dari bot
|
||||||
|
const botResponse = await getBotResponse(userText, chatHistory);
|
||||||
|
|
||||||
|
// Perbarui pesan bot dengan respons yang sebenarnya
|
||||||
|
if (messageTextContainer) {
|
||||||
|
messageTextContainer.innerHTML = botResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener untuk klik tombol
|
||||||
|
sendButton.addEventListener("click", sendMessage);
|
||||||
|
|
||||||
|
// Event listener untuk menekan Enter di textarea
|
||||||
|
textarea.addEventListener("keydown", function (event) {
|
||||||
|
if (event.key === "Enter" && !event.shiftKey) {
|
||||||
|
event.preventDefault(); // Mencegah newline di textarea
|
||||||
|
sendMessage(); // Panggil fungsi kirim pesan
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function getCurrentTime() {
|
||||||
|
const now = new Date();
|
||||||
|
return now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMessage(text, sender) {
|
||||||
|
const messageRow = document.createElement("div");
|
||||||
|
// Atur posisi berdasarkan sender (user -> end, bot -> start)
|
||||||
|
messageRow.classList.add("row", "flex-grow", "overflow-auto", sender === "user" ? "justify-content-end" : "justify-content-start");
|
||||||
|
|
||||||
|
const messageCol = document.createElement("div");
|
||||||
|
messageCol.classList.add("col-9", "w-auto");
|
||||||
|
|
||||||
|
// Atur lebar maksimum berdasarkan sender
|
||||||
|
messageCol.style.maxWidth = sender === "user" ? "50%" : "75%";
|
||||||
|
|
||||||
|
// Container untuk menyimpan nama dan bubble chat
|
||||||
|
const messageWrapper = document.createElement("div");
|
||||||
|
messageWrapper.classList.add("d-flex", "flex-column");
|
||||||
|
|
||||||
|
// Tambahkan Nama di luar bubble chat
|
||||||
|
const messageName = document.createElement("p");
|
||||||
|
messageName.classList.add("fw-bolder", sender === "user" ? "text-end" : "text-start", "mb-1");
|
||||||
|
messageName.textContent = sender === "user" ? "You" : "Neng Bedas";
|
||||||
|
|
||||||
|
// Bubble Chat
|
||||||
|
const messageContainer = document.createElement("div");
|
||||||
|
messageContainer.classList.add("p-2", "rounded", "mb-2", "d-inline-block");
|
||||||
|
if (sender === "user") {
|
||||||
|
messageContainer.classList.add("user-response", "bg-primary", "text-white");
|
||||||
|
} else {
|
||||||
|
messageContainer.classList.add("bot-response", "bg-light");
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageContent = document.createElement("div");
|
||||||
|
messageContent.classList.add("bot-message-text", "mb-0", "text-start");
|
||||||
|
messageContent.textContent = text;
|
||||||
|
|
||||||
|
// Waktu di dalam bubble chat
|
||||||
|
const messageTime = document.createElement("div");
|
||||||
|
messageTime.classList.add("sending-message-time", "text-end", "mt-1");
|
||||||
|
messageTime.innerHTML = `<p class="small mb-0 ${sender === "user" ? "text-white text-start" : "text-muted"}">${getCurrentTime()}</p>`;
|
||||||
|
|
||||||
|
messageContainer.appendChild(messageContent);
|
||||||
|
messageContainer.appendChild(messageTime);
|
||||||
|
|
||||||
|
// Jika pengirim adalah bot, tambahkan avatar
|
||||||
|
if (sender !== "user") {
|
||||||
|
const avatarContainer = document.createElement("div");
|
||||||
|
avatarContainer.classList.add("col-auto", "pe-0");
|
||||||
|
|
||||||
|
const avatarImg = document.createElement("img");
|
||||||
|
avatarImg.classList.add("rounded-circle");
|
||||||
|
avatarImg.width = 45;
|
||||||
|
avatarImg.src = "/images/iconchatbot.jpeg";
|
||||||
|
avatarImg.alt = "bot-avatar";
|
||||||
|
|
||||||
|
avatarContainer.appendChild(avatarImg);
|
||||||
|
messageRow.appendChild(avatarContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Masukkan nama dan bubble ke dalam wrapper
|
||||||
|
messageWrapper.appendChild(messageName);
|
||||||
|
messageWrapper.appendChild(messageContainer);
|
||||||
|
messageCol.appendChild(messageWrapper);
|
||||||
|
messageRow.appendChild(messageCol);
|
||||||
|
|
||||||
|
conversationArea.appendChild(messageRow);
|
||||||
|
conversationArea.scrollTop = conversationArea.scrollHeight;
|
||||||
|
|
||||||
|
return messageContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fungsi untuk memanggil API
|
||||||
|
async function getBotResponse(userText, historyChat) {
|
||||||
|
try {
|
||||||
|
const url = `${GlobalConfig.apiHost}/api/main-generate-text`;
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({prompt: userText, chatHistory: historyChat}),
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const rawBotResponse = data.nlpResponse;
|
||||||
|
// Tambahkan ke chatHistory
|
||||||
|
chatHistory.push({
|
||||||
|
user: userText,
|
||||||
|
rawBotResponse: rawBotResponse,
|
||||||
|
});
|
||||||
|
return data.response || "Maaf, saya tidak mengerti.";
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching bot response:", error);
|
||||||
|
return "Terjadi kesalahan, coba lagi nanti.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,11 +1,63 @@
|
|||||||
import GlobalConfig from "../global-config.js";
|
import GlobalConfig from "../global-config.js";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const tabs = document.querySelectorAll(".nav-link");
|
||||||
|
const timeElements = document.querySelectorAll(".sending-message-time p");
|
||||||
|
|
||||||
|
timeElements.forEach((element) => {
|
||||||
|
element.textContent = getCurrentTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
function activateTab(tab) {
|
||||||
|
tabs.forEach(btn => {
|
||||||
|
btn.classList.remove("border-3", "bg-primary", "text-white"); // Reset semua tab
|
||||||
|
});
|
||||||
|
tab.classList.add("border-3", "bg-primary", "text-white"); // Tambahkan warna pada tab aktif
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs.forEach(tab => {
|
||||||
|
tab.addEventListener("click", function () {
|
||||||
|
activateTab(this);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set warna awal untuk tab aktif (jika ada)
|
||||||
|
const initialActiveTab = document.querySelector(".nav-link.active");
|
||||||
|
if (initialActiveTab) {
|
||||||
|
activateTab(initialActiveTab);
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelectorAll(".nav-link").forEach(tab => {
|
document.querySelectorAll(".nav-link").forEach(tab => {
|
||||||
tab.addEventListener("click", function () {
|
tab.addEventListener("click", function () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const tab_active = getActiveTabId();
|
const tab_active = getActiveTabId();
|
||||||
console.log("Active Tab ID:", tab_active);
|
console.log("Active Tab ID:", tab_active);
|
||||||
|
|
||||||
|
// Hapus semua chat kecuali pesan default bot
|
||||||
|
conversationArea.innerHTML = `
|
||||||
|
<div class="row flex-grow overflow-auto align-items-start">
|
||||||
|
<!-- Avatar -->
|
||||||
|
<div class="col-auto alignpe-0">
|
||||||
|
<img class="rounded-circle" width="45" src="/images/iconchatbot.jpeg" alt="avatar-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nama dan Bubble Chat -->
|
||||||
|
<div class="col-9 w-auto">
|
||||||
|
<!-- Nama Bot -->
|
||||||
|
<p class="fw-bolder mb-1">Neng Bedas</p>
|
||||||
|
|
||||||
|
<!-- Bubble Chat -->
|
||||||
|
<div class="bot-response p-2 bg-light rounded mb-2 d-inline-block">
|
||||||
|
<p class="mb-0">Halo! Ada yang bisa saya bantu?</p>
|
||||||
|
|
||||||
|
<!-- Waktu (Tetap di Dalam Bubble Chat) -->
|
||||||
|
<div class="sending-message-time text-end mt-1">
|
||||||
|
<p class="text-muted small mb-0">Now</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
}, 100); // Timeout untuk memastikan class `active` sudah diperbarui
|
}, 100); // Timeout untuk memastikan class `active` sudah diperbarui
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -13,6 +65,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const textarea = document.getElementById("user-message");
|
const textarea = document.getElementById("user-message");
|
||||||
const sendButton = document.getElementById("send");
|
const sendButton = document.getElementById("send");
|
||||||
const conversationArea = document.querySelector(".row.flex-grow");
|
const conversationArea = document.querySelector(".row.flex-grow");
|
||||||
|
const chatHistory = [];
|
||||||
|
|
||||||
// Fungsi untuk mengirim pesan
|
// Fungsi untuk mengirim pesan
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
@@ -28,13 +81,20 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
addMessage(userText, "user");
|
addMessage(userText, "user");
|
||||||
|
|
||||||
// Tambahkan pesan bot sementara dengan "Loading..."
|
// Tambahkan pesan bot sementara dengan "Loading..."
|
||||||
const botMessageElement = addMessage('<div class="loader w-auto"></div>', "bot");
|
const botMessageElement = addMessage('<div class="bot-message-text">...</div>', "bot");
|
||||||
|
|
||||||
// Panggil API untuk mendapatkan response dari bot
|
const messageTextContainer = botMessageElement.querySelector(".bot-message-text");
|
||||||
const botResponse = await getBotResponse(currentTab, userText);
|
if (messageTextContainer) {
|
||||||
|
messageTextContainer.innerHTML = '<div class="loader ms-3"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panggil API untuk mendapatkan respons dari bot
|
||||||
|
const botResponse = await getBotResponse(currentTab, userText, chatHistory);
|
||||||
|
|
||||||
// Perbarui pesan bot dengan respons yang sebenarnya
|
// Perbarui pesan bot dengan respons yang sebenarnya
|
||||||
botMessageElement.innerHTML = botResponse;;
|
if (messageTextContainer) {
|
||||||
|
messageTextContainer.innerHTML = botResponse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,110 +109,86 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getCurrentTime() {
|
||||||
|
const now = new Date();
|
||||||
|
return now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
function addMessage(text, sender) {
|
function addMessage(text, sender) {
|
||||||
const messageRow = document.createElement("div");
|
const messageRow = document.createElement("div");
|
||||||
messageRow.classList.add("row", "flex-grow", "overflow-auto");
|
// Atur posisi berdasarkan sender (user -> end, bot -> start)
|
||||||
|
messageRow.classList.add("row", "flex-grow", "overflow-auto", sender === "user" ? "justify-content-end" : "justify-content-start");
|
||||||
|
|
||||||
const messageCol = document.createElement("div");
|
const messageCol = document.createElement("div");
|
||||||
messageCol.classList.add("w-auto", "d-inline-block"); // Menyesuaikan lebar konten
|
messageCol.classList.add("col-9", "w-auto");
|
||||||
|
|
||||||
|
// Atur lebar maksimum berdasarkan sender
|
||||||
|
messageCol.style.maxWidth = sender === "user" ? "50%" : "75%";
|
||||||
|
|
||||||
|
// Container untuk menyimpan nama dan bubble chat
|
||||||
|
const messageWrapper = document.createElement("div");
|
||||||
|
messageWrapper.classList.add("d-flex", "flex-column");
|
||||||
|
|
||||||
|
// Tambahkan Nama di luar bubble chat
|
||||||
|
const messageName = document.createElement("p");
|
||||||
|
messageName.classList.add("fw-bolder", sender === "user" ? "text-end" : "text-start", "mb-1");
|
||||||
|
messageName.textContent = sender === "user" ? "You" : "Neng Bedas";
|
||||||
|
|
||||||
|
// Bubble Chat
|
||||||
|
const messageContainer = document.createElement("div");
|
||||||
|
messageContainer.classList.add("p-2", "rounded", "mb-2", "d-inline-block");
|
||||||
if (sender === "user") {
|
if (sender === "user") {
|
||||||
messageCol.classList.add("ms-auto", "max-w-50"); // Rata kanan, max 50% (setara col-6)
|
messageContainer.classList.add("user-response", "bg-primary", "text-white");
|
||||||
} else {
|
} else {
|
||||||
messageCol.classList.add("max-w-75"); // Max 75% (setara col-9)
|
messageContainer.classList.add("bot-response", "bg-light");
|
||||||
|
}
|
||||||
|
|
||||||
// Tambahkan avatar hanya untuk bot
|
const messageContent = document.createElement("div");
|
||||||
const avatarSpan = document.createElement("span");
|
messageContent.classList.add("bot-message-text", "mb-0", "text-start");
|
||||||
avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
|
messageContent.textContent = text;
|
||||||
|
|
||||||
|
// Waktu di dalam bubble chat
|
||||||
|
const messageTime = document.createElement("div");
|
||||||
|
messageTime.classList.add("sending-message-time", "text-end", "mt-1");
|
||||||
|
messageTime.innerHTML = `<p class="small mb-0 ${sender === "user" ? "text-white text-start" : "text-muted"}">${getCurrentTime()}</p>`;
|
||||||
|
|
||||||
|
messageContainer.appendChild(messageContent);
|
||||||
|
messageContainer.appendChild(messageTime);
|
||||||
|
|
||||||
|
// Jika pengirim adalah bot, tambahkan avatar
|
||||||
|
if (sender !== "user") {
|
||||||
|
const avatarContainer = document.createElement("div");
|
||||||
|
avatarContainer.classList.add("col-auto", "pe-0");
|
||||||
|
|
||||||
const avatarImg = document.createElement("img");
|
const avatarImg = document.createElement("img");
|
||||||
avatarImg.classList.add("rounded-circle");
|
avatarImg.classList.add("rounded-circle");
|
||||||
avatarImg.width = 32;
|
avatarImg.width = 45;
|
||||||
avatarImg.src = "/images/iconchatbot.jpeg";
|
avatarImg.src = "/images/iconchatbot.jpeg";
|
||||||
avatarImg.alt = "bot-avatar";
|
avatarImg.alt = "bot-avatar";
|
||||||
|
|
||||||
avatarSpan.appendChild(avatarImg);
|
avatarContainer.appendChild(avatarImg);
|
||||||
messageCol.appendChild(avatarSpan);
|
messageRow.appendChild(avatarContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageDiv = document.createElement("div");
|
// Masukkan nama dan bubble ke dalam wrapper
|
||||||
messageDiv.classList.add("p-2", "rounded", "mb-2");
|
messageWrapper.appendChild(messageName);
|
||||||
|
messageWrapper.appendChild(messageContainer);
|
||||||
if (sender === "user") {
|
messageCol.appendChild(messageWrapper);
|
||||||
messageDiv.classList.add("user-response", "bg-primary", "text-white");
|
|
||||||
} else {
|
|
||||||
messageDiv.classList.add("bot-response", "bg-light");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
|
|
||||||
messageDiv.innerHTML = text;
|
|
||||||
messageCol.appendChild(messageDiv);
|
|
||||||
messageRow.appendChild(messageCol);
|
messageRow.appendChild(messageCol);
|
||||||
|
|
||||||
// Tambahkan ke area percakapan
|
|
||||||
conversationArea.appendChild(messageRow);
|
conversationArea.appendChild(messageRow);
|
||||||
|
|
||||||
// Scroll otomatis ke bawah
|
|
||||||
conversationArea.scrollTop = conversationArea.scrollHeight;
|
conversationArea.scrollTop = conversationArea.scrollHeight;
|
||||||
|
|
||||||
return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
|
return messageContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// function addMessage(text, sender) {
|
|
||||||
// const messageRow = document.createElement("div");
|
|
||||||
// messageRow.classList.add("row", "flex-grow", "overflow-auto");
|
|
||||||
|
|
||||||
// const messageCol = document.createElement("div");
|
|
||||||
// messageCol.classList.add("col-9", "w-auto");
|
|
||||||
|
|
||||||
// if (sender === "user") {
|
|
||||||
// messageCol.classList.add("ms-auto"); // Geser ke kanan untuk user
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const messageDiv = document.createElement("div");
|
|
||||||
// messageDiv.classList.add("p-2", "rounded", "mb-2");
|
|
||||||
|
|
||||||
// if (sender === "user") {
|
|
||||||
// messageDiv.classList.add("user-response", "bg-primary", "text-white");
|
|
||||||
// } else {
|
|
||||||
// messageDiv.classList.add("bot-response", "bg-light");
|
|
||||||
|
|
||||||
// // Tambahkan avatar hanya untuk bot
|
|
||||||
// const avatarSpan = document.createElement("span");
|
|
||||||
// avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
|
|
||||||
|
|
||||||
// const avatarImg = document.createElement("img");
|
|
||||||
// avatarImg.classList.add("rounded-circle");
|
|
||||||
// avatarImg.width = 32;
|
|
||||||
// avatarImg.src = "/images/iconchatbot.jpeg";
|
|
||||||
// avatarImg.alt = "bot-avatar";
|
|
||||||
|
|
||||||
// avatarSpan.appendChild(avatarImg);
|
|
||||||
// messageCol.appendChild(avatarSpan);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
|
|
||||||
// messageDiv.innerHTML = text;
|
|
||||||
// messageCol.appendChild(messageDiv);
|
|
||||||
// messageRow.appendChild(messageCol);
|
|
||||||
|
|
||||||
// // Tambahkan ke area percakapan
|
|
||||||
// conversationArea.appendChild(messageRow);
|
|
||||||
|
|
||||||
// // Scroll otomatis ke bawah
|
|
||||||
// conversationArea.scrollTop = conversationArea.scrollHeight;
|
|
||||||
|
|
||||||
// return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Fungsi untuk memanggil API
|
// Fungsi untuk memanggil API
|
||||||
async function getBotResponse(tab_active, userText) {
|
async function getBotResponse(tab_active, userText, historyChat) {
|
||||||
try {
|
try {
|
||||||
const url = `${GlobalConfig.apiHost}/api/generate-text`;
|
const url = `${GlobalConfig.apiHost}/api/generate-text`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({tab_active:tab_active, prompt: userText }),
|
body: JSON.stringify({tab_active:tab_active, prompt: userText, chatHistory: historyChat }),
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
@@ -162,6 +198,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
const rawBotResponse = data.nlpResponse;
|
||||||
|
// Tambahkan ke chatHistory
|
||||||
|
chatHistory.push({
|
||||||
|
user: userText,
|
||||||
|
rawBotResponse: rawBotResponse,
|
||||||
|
});
|
||||||
return data.response || "Maaf, saya tidak mengerti.";
|
return data.response || "Maaf, saya tidak mengerti.";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching bot response:", error);
|
console.error("Error fetching bot response:", error);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class Customers {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
|||||||
@@ -23,121 +23,9 @@ class BigData {
|
|||||||
}
|
}
|
||||||
async updateData(filterDate) {
|
async updateData(filterDate) {
|
||||||
try {
|
try {
|
||||||
console.log("Filtering data for date:", filterDate);
|
|
||||||
this.resumeBigData = await this.getBigDataResume(filterDate);
|
this.resumeBigData = await this.getBigDataResume(filterDate);
|
||||||
// this.totalTargetPAD = await this.getDataSettings("TARGET_PAD");
|
|
||||||
// this.resultDataTotal = await this.getDataTotalPotensi(year);
|
|
||||||
// this.dataVerification = await this.getDataVerfication(year);
|
|
||||||
// this.dataNonVerification = await this.getDataNonVerfication(year);
|
|
||||||
// this.dataBusiness = await this.getDataBusiness(year);
|
|
||||||
// this.dataNonBusiness = await this.getDataNonBusiness(year);
|
|
||||||
// this.dataTataRuang = await this.getDataSettings("TATA_RUANG");
|
|
||||||
// this.dataSumRealisasiTerbit = await this.getDataSettings(
|
|
||||||
// "REALISASI_TERBIT_PBG_SUM"
|
|
||||||
// );
|
|
||||||
// this.dataCountRealisasiTerbit = await this.getDataSettings(
|
|
||||||
// "REALISASI_TERBIT_PBG_COUNT"
|
|
||||||
// );
|
|
||||||
// this.dataSumMenungguKlikDPMPTSP = await this.getDataSettings(
|
|
||||||
// "MENUNGGU_KLIK_DPMPTSP_SUM"
|
|
||||||
// );
|
|
||||||
// this.dataCountMenungguKlikDPMPTSP = await this.getDataSettings(
|
|
||||||
// "MENUNGGU_KLIK_DPMPTSP_COUNT"
|
|
||||||
// );
|
|
||||||
// this.dataSumProsesDinasTeknis = await this.getDataSettings(
|
|
||||||
// "PROSES_DINAS_TEKNIS_SUM"
|
|
||||||
// );
|
|
||||||
// this.dataCountProsesDinasTeknis = await this.getDataSettings(
|
|
||||||
// "PROSES_DINAS_TEKNIS_COUNT"
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // total potensi
|
this.initChartTargetPAD(filterDate);
|
||||||
// this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
|
|
||||||
// this.bigTotalPotensi = new Big(this.resultDataTotal.totalData ?? 0);
|
|
||||||
|
|
||||||
// this.resultPercentage = 0;
|
|
||||||
// if (this.bigTotalPotensi > 0 && this.bigTargetPAD > 0) {
|
|
||||||
// this.resultPercentage = this.bigTotalPotensi
|
|
||||||
// .div(this.bigTargetPAD)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
// if (this.resultPercentage > 100) {
|
|
||||||
// this.resultPercentage = 100;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // tata ruang
|
|
||||||
// this.bigTotalTataRuang = new Big(this.dataTataRuang);
|
|
||||||
// this.percentageResultTataRuang =
|
|
||||||
// this.bigTotalTataRuang <= 0 || this.bigTotalPotensi <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.bigTotalTataRuang
|
|
||||||
// .div(this.bigTotalPotensi)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// // kekurangan potensi
|
|
||||||
// this.totalKekuranganPotensi = new Big(
|
|
||||||
// this.bigTargetPAD - this.bigTotalPotensi
|
|
||||||
// );
|
|
||||||
|
|
||||||
// this.percentageKekuranganPotensi =
|
|
||||||
// this.totalKekuranganPotensi <= 0 || this.bigTargetPAD <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.totalKekuranganPotensi
|
|
||||||
// .div(this.bigTargetPAD)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// // non-verification documents
|
|
||||||
// this.bigTotalNonVerification = new Big(
|
|
||||||
// this.dataNonVerification.total
|
|
||||||
// );
|
|
||||||
// this.percentageResultNonVerification =
|
|
||||||
// this.bigTotalNonVerification <= 0 || this.bigTotalPotensi <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.bigTotalNonVerification
|
|
||||||
// .div(this.bigTotalPotensi)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// // verification documents
|
|
||||||
// this.bigTotalVerification = new Big(this.dataVerification.total);
|
|
||||||
// this.percetageResultVerification =
|
|
||||||
// this.bigTotalVerification <= 0 || this.bigTotalPotensi <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.bigTotalVerification
|
|
||||||
// .div(this.bigTargetPAD)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// // business documents
|
|
||||||
// this.bigTotalBusiness = new Big(this.dataBusiness.total);
|
|
||||||
// this.percentageResultBusiness =
|
|
||||||
// this.bigTotalNonVerification <= 0 || this.bigTotalBusiness <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.bigTotalBusiness
|
|
||||||
// .div(this.bigTotalNonVerification)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// // non-business documents
|
|
||||||
// this.bigTotalNonBusiness = new Big(this.dataNonBusiness.total);
|
|
||||||
// this.percentageResultNonBusiness =
|
|
||||||
// this.bigTotalNonBusiness <= 0 ||
|
|
||||||
// this.bigTotalNonVerification <= 0
|
|
||||||
// ? 0
|
|
||||||
// : this.bigTotalNonBusiness
|
|
||||||
// .div(this.bigTotalNonVerification)
|
|
||||||
// .times(100)
|
|
||||||
// .toFixed(2);
|
|
||||||
|
|
||||||
// if (!this.bigTargetPAD) {
|
|
||||||
// console.error("Failed to load chart data");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.initChartTargetPAD();
|
|
||||||
this.initChartUsaha();
|
this.initChartUsaha();
|
||||||
this.initChartNonUsaha();
|
this.initChartNonUsaha();
|
||||||
this.initChartTotalPotensi();
|
this.initChartTotalPotensi();
|
||||||
@@ -180,190 +68,17 @@ class BigData {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async getDataTotalPotensi(year) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/all-task-documents?year=${year}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
initChartTargetPAD(filterDate) {
|
||||||
console.error("Network response was not ok", response);
|
const year =
|
||||||
}
|
filterDate === "latest"
|
||||||
|
? new Date().getFullYear()
|
||||||
const data = await response.json();
|
: new Date(filterDate).getFullYear();
|
||||||
return {
|
document
|
||||||
countData: data.data.count,
|
.querySelectorAll(".document-title.chart-target-pad")
|
||||||
totalData: data.data.total,
|
.forEach((element) => {
|
||||||
};
|
element.innerText = `Target PAD ${year}`;
|
||||||
} catch (error) {
|
});
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataVerfication(year) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/verification-documents?year=${year}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Network response was not ok", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return {
|
|
||||||
count: data.data.count,
|
|
||||||
total: data.data.total,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataNonVerfication(year) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/non-verification-documents?year=${year}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Network response was not ok", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return {
|
|
||||||
count: data.data.count,
|
|
||||||
total: data.data.total,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataBusiness(year) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/business-documents?year=${year}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Network response was not ok", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return {
|
|
||||||
count: data.data.count,
|
|
||||||
total: data.data.total,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataNonBusiness(year) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/non-business-documents?year=${year}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Network response was not ok", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return {
|
|
||||||
count: data.data.count,
|
|
||||||
total: data.data.total,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDataSettings(string_key) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`${GlobalConfig.apiHost}/api/api-data-settings?search=${string_key}`,
|
|
||||||
{
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${
|
|
||||||
document.querySelector("meta[name='api-token']")
|
|
||||||
.content
|
|
||||||
}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("Network response was not ok", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data.data[0].value;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching chart data:", error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initChartTargetPAD() {
|
|
||||||
document
|
document
|
||||||
.querySelectorAll(".document-count.chart-target-pad")
|
.querySelectorAll(".document-count.chart-target-pad")
|
||||||
.forEach((element) => {
|
.forEach((element) => {
|
||||||
@@ -373,7 +88,6 @@ class BigData {
|
|||||||
.querySelectorAll(".document-total.chart-target-pad")
|
.querySelectorAll(".document-total.chart-target-pad")
|
||||||
.forEach((element) => {
|
.forEach((element) => {
|
||||||
element.innerText = `Rp.${addThousandSeparators(
|
element.innerText = `Rp.${addThousandSeparators(
|
||||||
// this.bigTargetPAD.toString()
|
|
||||||
this.resumeBigData.target_pad.sum.toString()
|
this.resumeBigData.target_pad.sum.toString()
|
||||||
)}`;
|
)}`;
|
||||||
});
|
});
|
||||||
@@ -589,20 +303,18 @@ class BigData {
|
|||||||
document
|
document
|
||||||
.querySelectorAll(".document-count.chart-potensi-tata-ruang")
|
.querySelectorAll(".document-count.chart-potensi-tata-ruang")
|
||||||
.forEach((element) => {
|
.forEach((element) => {
|
||||||
element.innerText = "";
|
element.innerText = `${this.resumeBigData.tata_ruang.count}`;
|
||||||
});
|
});
|
||||||
document
|
document
|
||||||
.querySelectorAll(".document-total.chart-potensi-tata-ruang")
|
.querySelectorAll(".document-total.chart-potensi-tata-ruang")
|
||||||
.forEach((element) => {
|
.forEach((element) => {
|
||||||
element.innerText = `Rp.${addThousandSeparators(
|
element.innerText = `Rp.${addThousandSeparators(
|
||||||
// this.bigTotalTataRuang.toString()
|
|
||||||
this.resumeBigData.tata_ruang.sum.toString()
|
this.resumeBigData.tata_ruang.sum.toString()
|
||||||
)}`;
|
)}`;
|
||||||
});
|
});
|
||||||
document
|
document
|
||||||
.querySelectorAll(".small-percentage.chart-potensi-tata-ruang")
|
.querySelectorAll(".small-percentage.chart-potensi-tata-ruang")
|
||||||
.forEach((element) => {
|
.forEach((element) => {
|
||||||
// element.innerText = `${this.percentageResultTataRuang}%`;
|
|
||||||
element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`;
|
element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,19 @@ class LackOfPotential {
|
|||||||
this.pdamCount = this.allCountData.total_pdam ?? 0;
|
this.pdamCount = this.allCountData.total_pdam ?? 0;
|
||||||
this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0;
|
this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0;
|
||||||
|
|
||||||
|
let dataReportTourism = this.allCountData.data_report;
|
||||||
|
|
||||||
|
this.totalVilla = dataReportTourism
|
||||||
|
.filter((item) => item.kbli_title.toLowerCase() === "vila")
|
||||||
|
.reduce((sum, item) => sum + item.total_records, 0);
|
||||||
|
this.totalRestoran = dataReportTourism
|
||||||
|
.filter((item) => item.kbli_title.toLowerCase() === "restoran")
|
||||||
|
.reduce((sum, item) => sum + item.total_records, 0);
|
||||||
|
this.totalPariwisata = dataReportTourism.reduce(
|
||||||
|
(sum, item) => sum + item.total_records,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
|
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
|
||||||
this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0);
|
this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0);
|
||||||
this.bigTotalLackPotential = this.bigTargetPAD.minus(
|
this.bigTotalLackPotential = this.bigTargetPAD.minus(
|
||||||
@@ -140,6 +153,15 @@ class LackOfPotential {
|
|||||||
document.getElementById("pdam-count").innerText = this.pdamCount;
|
document.getElementById("pdam-count").innerText = this.pdamCount;
|
||||||
document.getElementById("pbb-bangunan-count").innerText =
|
document.getElementById("pbb-bangunan-count").innerText =
|
||||||
this.tataRuangCount;
|
this.tataRuangCount;
|
||||||
|
document.getElementById("tata-ruang-count").innerText =
|
||||||
|
this.tataRuangCount;
|
||||||
|
document.getElementById("tata-ruang-usaha-count").innerText =
|
||||||
|
this.tataRuangCount;
|
||||||
|
document.getElementById("restoran-count").innerText =
|
||||||
|
this.totalRestoran;
|
||||||
|
document.getElementById("villa-count").innerText = this.totalVilla;
|
||||||
|
document.getElementById("pariwisata-count").innerText =
|
||||||
|
this.totalPariwisata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.addEventListener("DOMContentLoaded", async function (e) {
|
document.addEventListener("DOMContentLoaded", async function (e) {
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
Loading...
|
Loading...
|
||||||
`;
|
`;
|
||||||
const isEdit = saveButton.classList.contains("btn-edit");
|
const isEdit = saveButton.classList.contains("btn-edit");
|
||||||
const formData = new FormData(form)
|
const formData = new FormData(form);
|
||||||
const toast = document.getElementById('toastEditUpdate');
|
const toast = document.getElementById("toastEditUpdate");
|
||||||
const toastBody = toast.querySelector('.toast-body');
|
const toastBody = toast.querySelector(".toast-body");
|
||||||
const toastHeader = toast.querySelector('.toast-header small');
|
const toastHeader = toast.querySelector(".toast-header small");
|
||||||
|
|
||||||
const data = {};
|
const data = {};
|
||||||
|
|
||||||
@@ -40,52 +40,54 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`,
|
.getAttribute("content")}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then((response) => response.json())
|
||||||
.then(data => {
|
.then((data) => {
|
||||||
if (!data.errors) {
|
if (!data.errors) {
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-check-square');
|
icon.classList.add("bx", "bxs-check-square");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'green'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "green"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set success message for the toast
|
// Set success message for the toast
|
||||||
toastBody.textContent = isEdit ? "Data updated successfully!" : "Data created successfully!";
|
toastBody.textContent = isEdit
|
||||||
toast.classList.add('show'); // Show the toast
|
? "Data updated successfully!"
|
||||||
|
: "Data created successfully!";
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = '/data/advertisements';
|
window.location.href = "/data/web-advertisements";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-error-alt');
|
icon.classList.add("bx", "bxs-error-alt");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
@@ -96,27 +98,28 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
||||||
|
|
||||||
// Set error message for the toast
|
// Set error message for the toast
|
||||||
toastBody.textContent = "Failed: " + (data.message || "Something went wrong");
|
toastBody.textContent =
|
||||||
toast.classList.add('show'); // Show the toast
|
"Failed: " + (data.message || "Something went wrong");
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(errors => {
|
.catch((errors) => {
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-error-alt');
|
icon.classList.add("bx", "bxs-error-alt");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
@@ -126,11 +129,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
||||||
|
|
||||||
// Set error message for the toast
|
// Set error message for the toast
|
||||||
toastBody.textContent = "An error occurred while processing your request.";
|
toastBody.textContent =
|
||||||
toast.classList.add('show'); // Show the toast
|
"An error occurred while processing your request.";
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -141,7 +145,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (inputValue.length < 2) return;
|
if (inputValue.length < 2) return;
|
||||||
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
|
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
|
||||||
|
|
||||||
let url = `${GlobalConfig.apiHost}/api/combobox/search-options?query=${encodeURIComponent(inputValue)}&field=${field}`;
|
let url = `${
|
||||||
|
GlobalConfig.apiHost
|
||||||
|
}/api/combobox/search-options?query=${encodeURIComponent(
|
||||||
|
inputValue
|
||||||
|
)}&field=${field}`;
|
||||||
|
|
||||||
// Jika field desa, tambahkan kecamatan sebagai filter
|
// Jika field desa, tambahkan kecamatan sebagai filter
|
||||||
if (field === "village_name") {
|
if (field === "village_name") {
|
||||||
@@ -149,30 +157,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`,
|
.getAttribute("content")}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then((response) => response.json())
|
||||||
.then(data => {
|
.then((data) => {
|
||||||
let dataList = document.getElementById(field + "Options");
|
let dataList = document.getElementById(field + "Options");
|
||||||
dataList.innerHTML = "";
|
dataList.innerHTML = "";
|
||||||
|
|
||||||
data.forEach(item => {
|
data.forEach((item) => {
|
||||||
let option = document.createElement("option");
|
let option = document.createElement("option");
|
||||||
option.value = item.name;
|
option.value = item.name;
|
||||||
option.dataset.code = item.code;
|
option.dataset.code = item.code;
|
||||||
dataList.appendChild(option);
|
dataList.appendChild(option);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => console.error("Error fetching options:", error));
|
.catch((error) => console.error("Error fetching options:", error));
|
||||||
};
|
};
|
||||||
|
|
||||||
document.querySelector('.btn-back').addEventListener('click', function() {
|
document.querySelector(".btn-back").addEventListener("click", function () {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -25,7 +25,7 @@ console.log(dropzonePreviewNode);
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
init: function () {
|
init: function () {
|
||||||
// Listen for the success event
|
// Listen for the success event
|
||||||
@@ -34,11 +34,12 @@ console.log(dropzonePreviewNode);
|
|||||||
console.log("API Response:", response);
|
console.log("API Response:", response);
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
showToast('bxs-check-square', 'green', response.message);
|
showToast("bxs-check-square", "green", response.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
// Tunggu sebentar lalu reload halaman
|
// Tunggu sebentar lalu reload halaman
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/advertisements";
|
window.location.href = "/data/web-advertisements";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
// Listen for the error event
|
// Listen for the error event
|
||||||
@@ -48,28 +49,30 @@ console.log(dropzonePreviewNode);
|
|||||||
// Handle the error response
|
// Handle the error response
|
||||||
|
|
||||||
// Show error toast
|
// Show error toast
|
||||||
showToast('bxs-error-alt', 'red', errorMessage.message);
|
showToast("bxs-error-alt", "red", errorMessage.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Add event listener to control the submission manually
|
// Add event listener to control the submission manually
|
||||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
document.querySelector("#submit-upload").addEventListener("click", function () {
|
||||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||||
const formData = new FormData()
|
const formData = new FormData();
|
||||||
console.log("Dropzonefiles", dropzone.files);
|
console.log("Dropzonefiles", dropzone.files);
|
||||||
|
|
||||||
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
this.innerHTML =
|
||||||
|
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
|
||||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
// Pastikan ada file dalam queue sebelum memprosesnya
|
||||||
if (dropzone.files.length > 0) {
|
if (dropzone.files.length > 0) {
|
||||||
formData.append('file', dropzone.files[0])
|
formData.append("file", dropzone.files[0]);
|
||||||
console.log("ini adalah form data on submit", ...formData);
|
console.log("ini adalah form data on submit", ...formData);
|
||||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||||
} else {
|
} else {
|
||||||
// Show error toast when no file is selected
|
// Show error toast when no file is selected
|
||||||
showToast('bxs-error-alt', 'red', "Please add a file first.");
|
showToast("bxs-error-alt", "red", "Please add a file first.");
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,17 +90,19 @@ dropzone.on("complete", function(file) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add event listener to donwload file template
|
// Add event listener to donwload file template
|
||||||
document.getElementById('downloadtempadvertisement').addEventListener('click', function() {
|
document
|
||||||
|
.getElementById("downloadtempadvertisement")
|
||||||
|
.addEventListener("click", function () {
|
||||||
var url = `${GlobalConfig.apiHost}/api/download-template-advertisement`;
|
var url = `${GlobalConfig.apiHost}/api/download-template-advertisement`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.blob();
|
return response.blob();
|
||||||
} else {
|
} else {
|
||||||
@@ -106,38 +111,42 @@ document.getElementById('downloadtempadvertisement').addEventListener('click', f
|
|||||||
})
|
})
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.style.display = 'none';
|
a.style.display = "none";
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'template_reklame.xlsx';
|
a.download = "template_reklame.xlsx";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Gagal mendownload file:", error);
|
console.error("Gagal mendownload file:", error);
|
||||||
showToast('bxs-error-alt', 'red', "Template file is not already exist.");
|
showToast(
|
||||||
})
|
"bxs-error-alt",
|
||||||
})
|
"red",
|
||||||
|
"Template file is not already exist."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Function to show toast
|
// Function to show toast
|
||||||
function showToast(iconClass, iconColor, message) {
|
function showToast(iconClass, iconColor, message) {
|
||||||
const toastElement = document.getElementById('toastUploadAdvertisement');
|
const toastElement = document.getElementById("toastUploadAdvertisement");
|
||||||
const toastBody = toastElement.querySelector('.toast-body');
|
const toastBody = toastElement.querySelector(".toast-body");
|
||||||
const toastHeader = toastElement.querySelector('.toast-header');
|
const toastHeader = toastElement.querySelector(".toast-header");
|
||||||
|
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
const existingIcon = toastHeader.querySelector('.bx');
|
const existingIcon = toastHeader.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon
|
toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new icon to the toast header
|
// Add the new icon to the toast header
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', iconClass);
|
icon.classList.add("bx", iconClass);
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = iconColor;
|
icon.style.color = iconColor;
|
||||||
toastHeader.querySelector('.auth-logo').appendChild(icon);
|
toastHeader.querySelector(".auth-logo").appendChild(icon);
|
||||||
|
|
||||||
// Set the toast message
|
// Set the toast message
|
||||||
toastBody.textContent = message;
|
toastBody.textContent = message;
|
||||||
@@ -146,4 +155,3 @@ function showToast(iconClass, iconColor, message) {
|
|||||||
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/spatial-plannings";
|
window.location.href = "/data/web-spatial-plannings";
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} else {
|
} else {
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ console.log(dropzonePreviewNode);
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
init: function () {
|
init: function () {
|
||||||
// Listen for the success event
|
// Listen for the success event
|
||||||
@@ -34,11 +34,12 @@ console.log(dropzonePreviewNode);
|
|||||||
console.log("API Response:", response);
|
console.log("API Response:", response);
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
showToast('bxs-check-square', 'green', response.message);
|
showToast("bxs-check-square", "green", response.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
// Tunggu sebentar lalu reload halaman
|
// Tunggu sebentar lalu reload halaman
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/spatial-plannings";
|
window.location.href = "/data/web-spatial-plannings";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
// Listen for the error event
|
// Listen for the error event
|
||||||
@@ -48,28 +49,30 @@ console.log(dropzonePreviewNode);
|
|||||||
// Handle the error response
|
// Handle the error response
|
||||||
|
|
||||||
// Show error toast
|
// Show error toast
|
||||||
showToast('bxs-error-alt', 'red', errorMessage.message);
|
showToast("bxs-error-alt", "red", errorMessage.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Add event listener to control the submission manually
|
// Add event listener to control the submission manually
|
||||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
document.querySelector("#submit-upload").addEventListener("click", function () {
|
||||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||||
const formData = new FormData()
|
const formData = new FormData();
|
||||||
console.log("Dropzonefiles", dropzone.files);
|
console.log("Dropzonefiles", dropzone.files);
|
||||||
|
|
||||||
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
this.innerHTML =
|
||||||
|
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
|
||||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
// Pastikan ada file dalam queue sebelum memprosesnya
|
||||||
if (dropzone.files.length > 0) {
|
if (dropzone.files.length > 0) {
|
||||||
formData.append('file', dropzone.files[0])
|
formData.append("file", dropzone.files[0]);
|
||||||
console.log("ini adalah form data on submit", ...formData);
|
console.log("ini adalah form data on submit", ...formData);
|
||||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||||
} else {
|
} else {
|
||||||
// Show error toast when no file is selected
|
// Show error toast when no file is selected
|
||||||
showToast('bxs-error-alt', 'red', "Please add a file first.");
|
showToast("bxs-error-alt", "red", "Please add a file first.");
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,17 +90,19 @@ dropzone.on("complete", function(file) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add event listener to download file template
|
// Add event listener to download file template
|
||||||
document.getElementById('downloadtempspatialPlannings').addEventListener('click', function() {
|
document
|
||||||
|
.getElementById("downloadtempspatialPlannings")
|
||||||
|
.addEventListener("click", function () {
|
||||||
var url = `${GlobalConfig.apiHost}/api/download-template-spatialPlannings`;
|
var url = `${GlobalConfig.apiHost}/api/download-template-spatialPlannings`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.blob(); // Jika respons OK, konversi menjadi blob
|
return response.blob(); // Jika respons OK, konversi menjadi blob
|
||||||
} else {
|
} else {
|
||||||
@@ -107,38 +112,42 @@ document.getElementById('downloadtempspatialPlannings').addEventListener('click'
|
|||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
console.log(blob);
|
console.log(blob);
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.style.display = 'none';
|
a.style.display = "none";
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'template_rencana_tata_ruang.xlsx';
|
a.download = "template_rencana_tata_ruang.xlsx";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Gagal mendownload file:", error);
|
console.error("Gagal mendownload file:", error);
|
||||||
showToast('bxs-error-alt', 'red', "Template file is not already exist.");
|
showToast(
|
||||||
})
|
"bxs-error-alt",
|
||||||
})
|
"red",
|
||||||
|
"Template file is not already exist."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Function to show toast
|
// Function to show toast
|
||||||
function showToast(iconClass, iconColor, message) {
|
function showToast(iconClass, iconColor, message) {
|
||||||
const toastElement = document.getElementById('toastUploadSpatialPlannings');
|
const toastElement = document.getElementById("toastUploadSpatialPlannings");
|
||||||
const toastBody = toastElement.querySelector('.toast-body');
|
const toastBody = toastElement.querySelector(".toast-body");
|
||||||
const toastHeader = toastElement.querySelector('.toast-header');
|
const toastHeader = toastElement.querySelector(".toast-header");
|
||||||
|
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
const existingIcon = toastHeader.querySelector('.bx');
|
const existingIcon = toastHeader.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon
|
toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new icon to the toast header
|
// Add the new icon to the toast header
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', iconClass);
|
icon.classList.add("bx", iconClass);
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = iconColor;
|
icon.style.color = iconColor;
|
||||||
toastHeader.querySelector('.auth-logo').appendChild(icon);
|
toastHeader.querySelector(".auth-logo").appendChild(icon);
|
||||||
|
|
||||||
// Set the toast message
|
// Set the toast message
|
||||||
toastBody.textContent = message;
|
toastBody.textContent = message;
|
||||||
@@ -147,4 +156,3 @@ function showToast(iconClass, iconColor, message) {
|
|||||||
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/tourisms";
|
window.location.href = "/data/web-tourisms";
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} else {
|
} else {
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ console.log(dropzonePreviewNode);
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
init: function () {
|
init: function () {
|
||||||
// Listen for the success event
|
// Listen for the success event
|
||||||
@@ -34,11 +34,12 @@ console.log(dropzonePreviewNode);
|
|||||||
console.log("API Response:", response);
|
console.log("API Response:", response);
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
showToast('bxs-check-square', 'green', response.message);
|
showToast("bxs-check-square", "green", response.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
// Tunggu sebentar lalu reload halaman
|
// Tunggu sebentar lalu reload halaman
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/tourisms";
|
window.location.href = "/data/web-tourisms";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
// Listen for the error event
|
// Listen for the error event
|
||||||
@@ -48,28 +49,30 @@ console.log(dropzonePreviewNode);
|
|||||||
// Handle the error response
|
// Handle the error response
|
||||||
|
|
||||||
// Show error toast
|
// Show error toast
|
||||||
showToast('bxs-error-alt', 'red', errorMessage.message);
|
showToast("bxs-error-alt", "red", errorMessage.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Add event listener to control the submission manually
|
// Add event listener to control the submission manually
|
||||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
document.querySelector("#submit-upload").addEventListener("click", function () {
|
||||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||||
const formData = new FormData()
|
const formData = new FormData();
|
||||||
console.log("Dropzonefiles", dropzone.files);
|
console.log("Dropzonefiles", dropzone.files);
|
||||||
|
|
||||||
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
this.innerHTML =
|
||||||
|
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
|
||||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
// Pastikan ada file dalam queue sebelum memprosesnya
|
||||||
if (dropzone.files.length > 0) {
|
if (dropzone.files.length > 0) {
|
||||||
formData.append('file', dropzone.files[0])
|
formData.append("file", dropzone.files[0]);
|
||||||
console.log("ini adalah form data on submit", ...formData);
|
console.log("ini adalah form data on submit", ...formData);
|
||||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||||
} else {
|
} else {
|
||||||
// Show error toast when no file is selected
|
// Show error toast when no file is selected
|
||||||
showToast('bxs-error-alt', 'red', "Please add a file first.");
|
showToast("bxs-error-alt", "red", "Please add a file first.");
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,17 +90,19 @@ dropzone.on("complete", function(file) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add event listener to download file template
|
// Add event listener to download file template
|
||||||
document.getElementById('downloadtemptourisms').addEventListener('click', function() {
|
document
|
||||||
|
.getElementById("downloadtemptourisms")
|
||||||
|
.addEventListener("click", function () {
|
||||||
var url = `${GlobalConfig.apiHost}/api/download-template-tourism`;
|
var url = `${GlobalConfig.apiHost}/api/download-template-tourism`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.blob(); // Jika respons OK, konversi menjadi blob
|
return response.blob(); // Jika respons OK, konversi menjadi blob
|
||||||
} else {
|
} else {
|
||||||
@@ -107,38 +112,42 @@ document.getElementById('downloadtemptourisms').addEventListener('click', functi
|
|||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
console.log(blob);
|
console.log(blob);
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.style.display = 'none';
|
a.style.display = "none";
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'template_pariwisata.xlsx';
|
a.download = "template_pariwisata.xlsx";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Gagal mendownload file:", error);
|
console.error("Gagal mendownload file:", error);
|
||||||
showToast('bxs-error-alt', 'red', "Template file is not already exist.");
|
showToast(
|
||||||
})
|
"bxs-error-alt",
|
||||||
})
|
"red",
|
||||||
|
"Template file is not already exist."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Function to show toast
|
// Function to show toast
|
||||||
function showToast(iconClass, iconColor, message) {
|
function showToast(iconClass, iconColor, message) {
|
||||||
const toastElement = document.getElementById('toastUploadTourisms');
|
const toastElement = document.getElementById("toastUploadTourisms");
|
||||||
const toastBody = toastElement.querySelector('.toast-body');
|
const toastBody = toastElement.querySelector(".toast-body");
|
||||||
const toastHeader = toastElement.querySelector('.toast-header');
|
const toastHeader = toastElement.querySelector(".toast-header");
|
||||||
|
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
const existingIcon = toastHeader.querySelector('.bx');
|
const existingIcon = toastHeader.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon
|
toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new icon to the toast header
|
// Add the new icon to the toast header
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', iconClass);
|
icon.classList.add("bx", iconClass);
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = iconColor;
|
icon.style.color = iconColor;
|
||||||
toastHeader.querySelector('.auth-logo').appendChild(icon);
|
toastHeader.querySelector(".auth-logo").appendChild(icon);
|
||||||
|
|
||||||
// Set the toast message
|
// Set the toast message
|
||||||
toastBody.textContent = message;
|
toastBody.textContent = message;
|
||||||
@@ -147,4 +156,3 @@ function showToast(iconClass, iconColor, message) {
|
|||||||
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
Loading...
|
Loading...
|
||||||
`;
|
`;
|
||||||
const isEdit = saveButton.classList.contains("btn-edit");
|
const isEdit = saveButton.classList.contains("btn-edit");
|
||||||
const formData = new FormData(form)
|
const formData = new FormData(form);
|
||||||
const toast = document.getElementById('toastEditUpdate');
|
const toast = document.getElementById("toastEditUpdate");
|
||||||
const toastBody = toast.querySelector('.toast-body');
|
const toastBody = toast.querySelector(".toast-body");
|
||||||
const toastHeader = toast.querySelector('.toast-header small');
|
const toastHeader = toast.querySelector(".toast-header small");
|
||||||
|
|
||||||
const data = {};
|
const data = {};
|
||||||
|
|
||||||
@@ -40,53 +40,55 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`,
|
.getAttribute("content")}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then((response) => response.json())
|
||||||
.then(data => {
|
.then((data) => {
|
||||||
console.log("Response data:", data);
|
console.log("Response data:", data);
|
||||||
if (!data.errors) {
|
if (!data.errors) {
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-check-square');
|
icon.classList.add("bx", "bxs-check-square");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'green'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "green"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set success message for the toast
|
// Set success message for the toast
|
||||||
toastBody.textContent = isEdit ? "Data updated successfully!" : "Data created successfully!";
|
toastBody.textContent = isEdit
|
||||||
toast.classList.add('show'); // Show the toast
|
? "Data updated successfully!"
|
||||||
|
: "Data created successfully!";
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = '/data/umkm';
|
window.location.href = "/data/web-umkm";
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-error-alt');
|
icon.classList.add("bx", "bxs-error-alt");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
@@ -96,30 +98,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
modalButton.disabled = false;
|
modalButton.disabled = false;
|
||||||
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
||||||
|
|
||||||
|
|
||||||
// Set error message for the toast
|
// Set error message for the toast
|
||||||
toastBody.textContent = "Error: " + (data.message || "Something went wrong");
|
toastBody.textContent =
|
||||||
toast.classList.add('show'); // Show the toast
|
"Error: " + (data.message || "Something went wrong");
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
if (authLogo) {
|
if (authLogo) {
|
||||||
// Hapus ikon yang sudah ada jika ada
|
// Hapus ikon yang sudah ada jika ada
|
||||||
const existingIcon = authLogo.querySelector('.bx');
|
const existingIcon = authLogo.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
authLogo.removeChild(existingIcon);
|
authLogo.removeChild(existingIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buat ikon baru
|
// Buat ikon baru
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', 'bxs-error-alt');
|
icon.classList.add("bx", "bxs-error-alt");
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
|
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
|
||||||
|
|
||||||
// Tambahkan ikon ke dalam auth-logo
|
// Tambahkan ikon ke dalam auth-logo
|
||||||
authLogo.appendChild(icon);
|
authLogo.appendChild(icon);
|
||||||
@@ -130,11 +132,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
modalButton.innerHTML = isEdit ? "Update" : "Create";
|
||||||
|
|
||||||
// Set error message for the toast
|
// Set error message for the toast
|
||||||
toastBody.textContent = "An error occurred while processing your request.";
|
toastBody.textContent =
|
||||||
toast.classList.add('show'); // Show the toast
|
"An error occurred while processing your request.";
|
||||||
|
toast.classList.add("show"); // Show the toast
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
toast.classList.remove('show'); // Hide the toast after 3 seconds
|
toast.classList.remove("show"); // Hide the toast after 3 seconds
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -145,7 +148,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (inputValue.length < 2) return;
|
if (inputValue.length < 2) return;
|
||||||
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
|
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
|
||||||
|
|
||||||
let url = `${GlobalConfig.apiHost}/api/combobox/search-options?query=${encodeURIComponent(inputValue)}&field=${field}`;
|
let url = `${
|
||||||
|
GlobalConfig.apiHost
|
||||||
|
}/api/combobox/search-options?query=${encodeURIComponent(
|
||||||
|
inputValue
|
||||||
|
)}&field=${field}`;
|
||||||
|
|
||||||
// Jika field desa, tambahkan kecamatan sebagai filter
|
// Jika field desa, tambahkan kecamatan sebagai filter
|
||||||
if (field === "village_name") {
|
if (field === "village_name") {
|
||||||
@@ -153,30 +160,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`,
|
.getAttribute("content")}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then((response) => response.json())
|
||||||
.then(data => {
|
.then((data) => {
|
||||||
let dataList = document.getElementById(field + "Options");
|
let dataList = document.getElementById(field + "Options");
|
||||||
dataList.innerHTML = "";
|
dataList.innerHTML = "";
|
||||||
|
|
||||||
data.forEach(item => {
|
data.forEach((item) => {
|
||||||
let option = document.createElement("option");
|
let option = document.createElement("option");
|
||||||
option.value = item.name;
|
option.value = item.name;
|
||||||
option.dataset.code = item.code;
|
option.dataset.code = item.code;
|
||||||
dataList.appendChild(option);
|
dataList.appendChild(option);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => console.error("Error fetching options:", error));
|
.catch((error) => console.error("Error fetching options:", error));
|
||||||
};
|
};
|
||||||
|
|
||||||
document.querySelector('.btn-back').addEventListener('click', function() {
|
document.querySelector(".btn-back").addEventListener("click", function () {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -25,7 +25,7 @@ console.log(dropzonePreviewNode);
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
init: function () {
|
init: function () {
|
||||||
// Listen for the success event
|
// Listen for the success event
|
||||||
@@ -34,11 +34,12 @@ console.log(dropzonePreviewNode);
|
|||||||
console.log("API Response:", response);
|
console.log("API Response:", response);
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
showToast('bxs-check-square', 'green', response.message);
|
showToast("bxs-check-square", "green", response.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
// Tunggu sebentar lalu reload halaman
|
// Tunggu sebentar lalu reload halaman
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = "/data/umkm";
|
window.location.href = "/data/web-umkm";
|
||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
// Listen for the error event
|
// Listen for the error event
|
||||||
@@ -48,28 +49,30 @@ console.log(dropzonePreviewNode);
|
|||||||
// Handle the error response
|
// Handle the error response
|
||||||
|
|
||||||
// Show error toast
|
// Show error toast
|
||||||
showToast('bxs-error-alt', 'red', errorMessage.message);
|
showToast("bxs-error-alt", "red", errorMessage.message);
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML =
|
||||||
|
"Upload Files";
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Add event listener to control the submission manually
|
// Add event listener to control the submission manually
|
||||||
document.querySelector("#submit-upload").addEventListener("click", function () {
|
document.querySelector("#submit-upload").addEventListener("click", function () {
|
||||||
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
console.log("Ini adalah value dropzone", dropzone.files[0]);
|
||||||
const formData = new FormData()
|
const formData = new FormData();
|
||||||
console.log("Dropzonefiles", dropzone.files);
|
console.log("Dropzonefiles", dropzone.files);
|
||||||
|
|
||||||
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
this.innerHTML =
|
||||||
|
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
|
||||||
|
|
||||||
// Pastikan ada file dalam queue sebelum memprosesnya
|
// Pastikan ada file dalam queue sebelum memprosesnya
|
||||||
if (dropzone.files.length > 0) {
|
if (dropzone.files.length > 0) {
|
||||||
formData.append('file', dropzone.files[0])
|
formData.append("file", dropzone.files[0]);
|
||||||
console.log("ini adalah form data on submit", ...formData);
|
console.log("ini adalah form data on submit", ...formData);
|
||||||
dropzone.processQueue(); // Ini akan manual memicu upload
|
dropzone.processQueue(); // Ini akan manual memicu upload
|
||||||
} else {
|
} else {
|
||||||
// Show error toast when no file is selected
|
// Show error toast when no file is selected
|
||||||
showToast('bxs-error-alt', 'red', "Please add a file first.");
|
showToast("bxs-error-alt", "red", "Please add a file first.");
|
||||||
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
document.getElementById("submit-upload").innerHTML = "Upload Files";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -87,17 +90,19 @@ dropzone.on("complete", function(file) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add event listener to download file template
|
// Add event listener to download file template
|
||||||
document.getElementById('downloadtempumkm').addEventListener('click', function() {
|
document
|
||||||
|
.getElementById("downloadtempumkm")
|
||||||
|
.addEventListener("click", function () {
|
||||||
var url = `${GlobalConfig.apiHost}/api/download-template-umkm`;
|
var url = `${GlobalConfig.apiHost}/api/download-template-umkm`;
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${document
|
||||||
.querySelector('meta[name="api-token"]')
|
.querySelector('meta[name="api-token"]')
|
||||||
.getAttribute("content")}`
|
.getAttribute("content")}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.blob(); // Jika respons OK, konversi menjadi blob
|
return response.blob(); // Jika respons OK, konversi menjadi blob
|
||||||
} else {
|
} else {
|
||||||
@@ -106,38 +111,42 @@ document.getElementById('downloadtempumkm').addEventListener('click', function()
|
|||||||
})
|
})
|
||||||
.then((blob) => {
|
.then((blob) => {
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement('a');
|
const a = document.createElement("a");
|
||||||
a.style.display = 'none';
|
a.style.display = "none";
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'template_umkm.xlsx';
|
a.download = "template_umkm.xlsx";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Gagal mendownload file:", error);
|
console.error("Gagal mendownload file:", error);
|
||||||
showToast('bxs-error-alt', 'red', "Template file is not already exist.");
|
showToast(
|
||||||
})
|
"bxs-error-alt",
|
||||||
})
|
"red",
|
||||||
|
"Template file is not already exist."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Function to show toast
|
// Function to show toast
|
||||||
function showToast(iconClass, iconColor, message) {
|
function showToast(iconClass, iconColor, message) {
|
||||||
const toastElement = document.getElementById('toastUploadUmkm');
|
const toastElement = document.getElementById("toastUploadUmkm");
|
||||||
const toastBody = toastElement.querySelector('.toast-body');
|
const toastBody = toastElement.querySelector(".toast-body");
|
||||||
const toastHeader = toastElement.querySelector('.toast-header');
|
const toastHeader = toastElement.querySelector(".toast-header");
|
||||||
|
|
||||||
// Remove existing icon (if any) before adding the new one
|
// Remove existing icon (if any) before adding the new one
|
||||||
const existingIcon = toastHeader.querySelector('.bx');
|
const existingIcon = toastHeader.querySelector(".bx");
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon
|
toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new icon to the toast header
|
// Add the new icon to the toast header
|
||||||
const icon = document.createElement('i');
|
const icon = document.createElement("i");
|
||||||
icon.classList.add('bx', iconClass);
|
icon.classList.add("bx", iconClass);
|
||||||
icon.style.fontSize = '25px';
|
icon.style.fontSize = "25px";
|
||||||
icon.style.color = iconColor;
|
icon.style.color = iconColor;
|
||||||
toastHeader.querySelector('.auth-logo').appendChild(icon);
|
toastHeader.querySelector(".auth-logo").appendChild(icon);
|
||||||
|
|
||||||
// Set the toast message
|
// Set the toast message
|
||||||
toastBody.textContent = message;
|
toastBody.textContent = message;
|
||||||
@@ -146,4 +155,3 @@ function showToast(iconClass, iconColor, message) {
|
|||||||
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
79
resources/js/maps/maps-kml.js
Normal file
79
resources/js/maps/maps-kml.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import L from "leaflet";
|
||||||
|
import "leaflet/dist/leaflet.css";
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
var map = L.map("map").setView([-6.9175, 107.6191], 10); // Bandung
|
||||||
|
|
||||||
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
|
attribution: "© OpenStreetMap contributors",
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Dapatkan elemen loading
|
||||||
|
const loadingDiv = document.getElementById("loading");
|
||||||
|
loadingDiv.style.display = "flex"; // Tampilkan loading
|
||||||
|
|
||||||
|
fetch("/storage/maps/rencana-polaruang.geojson")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((geojson) => {
|
||||||
|
let colorMapping = {
|
||||||
|
BJ: "rgb(235, 30, 30)",
|
||||||
|
BA: "rgb(151, 219, 242)",
|
||||||
|
CA: "rgb(70, 70, 165)",
|
||||||
|
"P-2": "rgb(230, 255, 75)",
|
||||||
|
HL: "rgb(50, 95, 40)",
|
||||||
|
HPT: "rgb(75, 155, 55)",
|
||||||
|
HP: "rgb(125, 180, 55)",
|
||||||
|
W: "rgb(255, 165, 255)",
|
||||||
|
PTL: "rgb(0, 255, 205)",
|
||||||
|
"IK-2": "rgb(130, 185, 210)",
|
||||||
|
"P-3": "rgb(175, 175, 55)",
|
||||||
|
PS: "rgb(5, 215, 215)",
|
||||||
|
PD: "rgb(235, 155, 60)",
|
||||||
|
PK: "rgb(245, 155, 30)",
|
||||||
|
HK: "rgb(155, 0, 255)",
|
||||||
|
KPI: "rgb(105, 0, 0)",
|
||||||
|
MBT: "rgb(95, 115, 145)",
|
||||||
|
"P-4": "rgb(185, 235, 185)",
|
||||||
|
TB: "rgb(70, 150, 255)",
|
||||||
|
"P-1": "rgb(200, 245, 70)",
|
||||||
|
TR: "rgb(215, 55, 0)",
|
||||||
|
THR: "rgb(185, 165, 255)",
|
||||||
|
TWA: "rgb(210, 190, 255)",
|
||||||
|
};
|
||||||
|
var geoLayer = L.geoJSON(geojson, {
|
||||||
|
style: function (feature) {
|
||||||
|
let htmlString = feature.properties.description.toString();
|
||||||
|
|
||||||
|
let match = htmlString.match(
|
||||||
|
/<td>Kode Kawasan<\/td>\s*<td>(.*?)<\/td>/
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Kode Kawasan ", match[1]);
|
||||||
|
|
||||||
|
let color_code = match[1];
|
||||||
|
|
||||||
|
return {
|
||||||
|
color: colorMapping[color_code],
|
||||||
|
fillColor: colorMapping[color_code] || "#cccccc",
|
||||||
|
fillOpacity: 0.6,
|
||||||
|
weight: 1.5,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
onEachFeature: function (feature, layer) {
|
||||||
|
if (feature.properties && feature.properties.name) {
|
||||||
|
layer.bindPopup(feature.properties.name);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
map.fitBounds(geoLayer.getBounds());
|
||||||
|
|
||||||
|
// Sembunyikan loading setelah selesai render
|
||||||
|
loadingDiv.style.display = "none";
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error loading GeoJSON:", error);
|
||||||
|
loadingDiv.innerHTML =
|
||||||
|
"<div class='loading-text' style='background: red;'>Failed to load data!</div>";
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -31,7 +31,7 @@ class UsersTable {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class Menus {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Roles {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
|
|||||||
@@ -4,13 +4,21 @@ import "gridjs/dist/gridjs.umd.js";
|
|||||||
import GlobalConfig from "../../global-config.js";
|
import GlobalConfig from "../../global-config.js";
|
||||||
|
|
||||||
class SyncronizeTask {
|
class SyncronizeTask {
|
||||||
|
constructor() {
|
||||||
|
this.toastElement = document.getElementById("toastNotification");
|
||||||
|
this.toastMessage = document.getElementById("toast-message");
|
||||||
|
this.toast = new bootstrap.Toast(this.toastElement);
|
||||||
|
this.table = null;
|
||||||
|
}
|
||||||
init() {
|
init() {
|
||||||
this.initTableImportDatasources();
|
this.initTableImportDatasources();
|
||||||
this.handleSubmitSync();
|
this.handleSubmitSync();
|
||||||
this.handleSubmitSnycGoogleSheet();
|
|
||||||
}
|
}
|
||||||
initTableImportDatasources() {
|
initTableImportDatasources() {
|
||||||
new Grid({
|
let tableContainer = document.getElementById(
|
||||||
|
"table-import-datasources"
|
||||||
|
);
|
||||||
|
this.table = new gridjs.Grid({
|
||||||
columns: ["ID", "Message", "Response", "Status", "Created"],
|
columns: ["ID", "Message", "Response", "Status", "Created"],
|
||||||
search: {
|
search: {
|
||||||
server: {
|
server: {
|
||||||
@@ -18,7 +26,7 @@ class SyncronizeTask {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 15,
|
limit: 50,
|
||||||
server: {
|
server: {
|
||||||
url: (prev, page) =>
|
url: (prev, page) =>
|
||||||
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
`${prev}${prev.includes("?") ? "&" : "?"}page=${
|
||||||
@@ -45,20 +53,24 @@ class SyncronizeTask {
|
|||||||
]),
|
]),
|
||||||
total: (data) => data.meta.total,
|
total: (data) => data.meta.total,
|
||||||
},
|
},
|
||||||
}).render(document.getElementById("table-import-datasources"));
|
}).render(tableContainer);
|
||||||
}
|
}
|
||||||
handleSubmitSync() {
|
handleSubmitSync() {
|
||||||
const button = document.getElementById("btn-sync-submit");
|
const button = document.getElementById("btn-sync-submit");
|
||||||
|
const spinner = document.getElementById("spinner");
|
||||||
|
const apiToken = document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content");
|
||||||
|
|
||||||
|
// Show the spinner while checking
|
||||||
|
spinner.classList.remove("d-none");
|
||||||
|
|
||||||
// Check if the button should be enabled or disabled based on the status
|
|
||||||
fetch(
|
fetch(
|
||||||
`${GlobalConfig.apiHost}/api/import-datasource/check-datasource`,
|
`${GlobalConfig.apiHost}/api/import-datasource/check-datasource`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${apiToken}`,
|
||||||
.querySelector('meta[name="api-token"]')
|
|
||||||
.getAttribute("content")}`,
|
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -70,91 +82,74 @@ class SyncronizeTask {
|
|||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
console.log("data check button sync", data.can_execute);
|
|
||||||
button.disabled = !data.can_execute;
|
button.disabled = !data.can_execute;
|
||||||
|
|
||||||
// If the button is enabled, add click event to trigger sync
|
if (!data.can_execute) {
|
||||||
if (!button.disabled) {
|
// Keep spinner visible if cannot execute
|
||||||
button.addEventListener("click", function (e) {
|
spinner.classList.remove("d-none");
|
||||||
button.disabled = true; // Disable button to prevent multiple clicks
|
} else {
|
||||||
button.textContent = "Syncing..."; // Change button text to show syncing
|
// Hide spinner when execution is allowed
|
||||||
|
spinner.classList.add("d-none");
|
||||||
|
|
||||||
// Trigger the scraping API call
|
// Remove previous event listener before adding a new one
|
||||||
fetch(`${GlobalConfig.apiHost}/api/scraping`, {
|
button.removeEventListener("click", this.handleSyncClick);
|
||||||
method: "GET",
|
button.addEventListener(
|
||||||
headers: {
|
"click",
|
||||||
Authorization: `Bearer ${document
|
this.handleSyncClick.bind(this)
|
||||||
.querySelector('meta[name="api-token"]')
|
|
||||||
.getAttribute("content")}`,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
"Network response was not ok"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
console.log("data sync button", data);
|
|
||||||
alert("Synchronization executed successfully");
|
|
||||||
window.location.reload();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Fetch error:", err);
|
|
||||||
alert(
|
|
||||||
"An error occurred during synchronization"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
button.disabled = false; // Re-enable the button after the request is complete
|
|
||||||
button.textContent = "Sync Data"; // Reset button text
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Fetch error:", err);
|
console.error("Fetch error:", err);
|
||||||
alert("An error occurred while checking the datasource");
|
alert("An error occurred while checking the datasource");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleSubmitSnycGoogleSheet() {
|
|
||||||
const button = document.getElementById("btn-sync-submit-google-sheet");
|
|
||||||
button.addEventListener("click", function (e) {
|
|
||||||
button.disabled = true; // Disable button to prevent multiple clicks
|
|
||||||
button.textContent = "Syncing..."; // Change button text to show syncing
|
|
||||||
|
|
||||||
// Trigger the scraping API call
|
handleSyncClick() {
|
||||||
fetch(`${GlobalConfig.apiHost}/api/sync-pbg-task-google-sheet`, {
|
const button = document.getElementById("btn-sync-submit");
|
||||||
|
const spinner = document.getElementById("spinner");
|
||||||
|
const apiToken = document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content");
|
||||||
|
|
||||||
|
button.disabled = true; // Prevent multiple clicks
|
||||||
|
spinner.classList.remove("d-none"); // Show spinner during sync
|
||||||
|
|
||||||
|
fetch(`${GlobalConfig.apiHost}/api/scraping`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${document
|
Authorization: `Bearer ${apiToken}`,
|
||||||
.querySelector('meta[name="api-token"]')
|
|
||||||
.getAttribute("content")}`,
|
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then(async (response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Network response was not ok");
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = await response.json();
|
||||||
|
} catch (jsonError) {
|
||||||
|
throw new Error("Failed to parse JSON response");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
console.log("data sync button", data);
|
this.toastMessage.innerText =
|
||||||
alert("Synchronization executed successfully");
|
data.data.message || "Synchronize successfully!";
|
||||||
window.location.reload();
|
this.toast.show();
|
||||||
|
|
||||||
|
// Update the table if it exists
|
||||||
|
if (this.table) {
|
||||||
|
this.table.updateConfig({}).forceRender();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Fetch error:", err);
|
console.error("Fetch error:", err);
|
||||||
alert("An error occurred during synchronization");
|
alert("An error occurred during synchronization" + err.message);
|
||||||
})
|
button.disabled = false;
|
||||||
.finally(() => {
|
|
||||||
button.disabled = false; // Re-enable the button after the request is complete
|
|
||||||
button.textContent = "Sync Google Sheet"; // Reset button text
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
// overflow: hidden;
|
// overflow: hidden;
|
||||||
|
|
||||||
.circle-content {
|
.circle-content {
|
||||||
width: 180px; /* Ukuran lingkaran dalam */
|
min-width: 180px; /* Ukuran lingkaran dalam */
|
||||||
height: 180px;
|
min-height: 180px;
|
||||||
background-color: var(--circle-color); /* Warna lingkaran dalam */
|
background-color: var(--circle-color); /* Warna lingkaran dalam */
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -57,17 +57,20 @@
|
|||||||
padding: 0 7px;
|
padding: 0 7px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.circle-content .document-count {
|
.circle-content .document-count {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.circle-content .document-type {
|
.circle-content .document-type {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-circle-container {
|
.small-circle-container {
|
||||||
|
|||||||
104
resources/views/bigdata-resumes/index.blade.php
Normal file
104
resources/views/bigdata-resumes/index.blade.php
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
@extends('layouts.vertical', ['subtitle' => 'Laporan Pimpinan'])
|
||||||
|
|
||||||
|
@section('css')
|
||||||
|
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
|
||||||
|
<style>
|
||||||
|
/* Ensure the Grid.js container allows full scrolling */
|
||||||
|
#table-bigdata-resumes {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden; /* Prevent vertical scrolling */
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100%; /* Adjust height if necessary */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure Grid.js wrapper is scrollable */
|
||||||
|
.gridjs-wrapper {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make the entire scrollbar much bigger */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar {
|
||||||
|
height: 40px; /* Increase scrollbar height */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar track (background) */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-track {
|
||||||
|
background: #ddd;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar thumb (draggable part) */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-thumb {
|
||||||
|
background: #007bff;
|
||||||
|
border-radius: 20px;
|
||||||
|
width: 40px; /* Wider scrollbar thumb */
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar thumb hover effect */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bigger Scrollbar Buttons */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-button {
|
||||||
|
background: #007bff;
|
||||||
|
height: 40px; /* Force bigger button height */
|
||||||
|
width: 40px; /* Force bigger button width */
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Left Scroll Button */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-button:horizontal:decrement {
|
||||||
|
display: block;
|
||||||
|
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M15 18l-6-6 6-6"/></svg>') no-repeat center;
|
||||||
|
background-size: 30px;
|
||||||
|
width: 40px; /* Ensure button size */
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Right Scroll Button */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-button:horizontal:increment {
|
||||||
|
display: block;
|
||||||
|
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><path d="M9 18l6-6-6-6"/></svg>') no-repeat center;
|
||||||
|
background-size: 30px;
|
||||||
|
width: 40px; /* Ensure button size */
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar button hover effect */
|
||||||
|
.gridjs-wrapper::-webkit-scrollbar-button:hover {
|
||||||
|
background: #0056b3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
@include('layouts.partials/page-title', ['title' => 'Laporan', 'subtitle' => 'Laporan Pimpinan'])
|
||||||
|
|
||||||
|
<x-toast-notification />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card w-100 h-100">
|
||||||
|
<div class="card-header d-flex align-items-center">
|
||||||
|
<input type="text" class="form-control me-2 w-auto" />
|
||||||
|
<button id="search-btn" class="btn btn-md btn-info text-white">Cari</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="table-bigdata-resumes"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
@vite(['resources/js/bigdata-resumes/index.js'])
|
||||||
|
@endsection
|
||||||
@@ -64,7 +64,10 @@
|
|||||||
<!-- end dropzon-preview -->
|
<!-- end dropzon-preview -->
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<button id="submit-upload" class="btn btn-primary">Upload Files</button>
|
<button type="button" class="btn btn-primary" id="btnUploadBusinessIndustry">
|
||||||
|
<span id="spinner" class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
|
||||||
|
Upload
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- end card body -->
|
</div> <!-- end card body -->
|
||||||
</div> <!-- end card -->
|
</div> <!-- end card -->
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Business Industries'])
|
@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Business Industries'])
|
||||||
|
|
||||||
<x-toast-notification />
|
<x-toast-notification />
|
||||||
<x-modal-confirmation buttonText="Delete" confirmationMessage="Are you sure you want to delete this?" />
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|||||||
77
resources/views/chatbot-pimpinan/index.blade.php
Normal file
77
resources/views/chatbot-pimpinan/index.blade.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
@extends('layouts.vertical', ['subtitle' => 'Main Chatbot'])
|
||||||
|
|
||||||
|
@section('css')
|
||||||
|
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
|
||||||
|
<style>
|
||||||
|
#user-message {
|
||||||
|
height: 60px; /* Menambah tinggi textarea */
|
||||||
|
font-size: 1.1rem; /* Memperbesar font */
|
||||||
|
padding: 10px; /* Menambah ruang di dalam textarea */
|
||||||
|
resize: none; /* Mencegah resize manual */
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
width: 10px;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: l5 1s infinite linear alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes l5 {
|
||||||
|
0% {box-shadow: 10px 0 #000, -10px 0 #0002; background: #000 }
|
||||||
|
33% {box-shadow: 10px 0 #000, -10px 0 #0002; background: #0002}
|
||||||
|
66% {box-shadow: 10px 0 #0002, -10px 0 #000; background: #0002}
|
||||||
|
100%{box-shadow: 10px 0 #0002, -10px 0 #000; background: #000 }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('layouts.partials/page-title', ['title' => 'Main Chatbot', 'subtitle' => 'Main Chatbot'])
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body d-flex flex-column" style="height: 700px;">
|
||||||
|
<!-- Conversation Area -->
|
||||||
|
|
||||||
|
<!-- Bot Response -->
|
||||||
|
<div class="row flex-grow overflow-auto align-items-start">
|
||||||
|
<!-- Avatar -->
|
||||||
|
<div class="col-auto alignpe-0">
|
||||||
|
<img class="rounded-circle" width="45" src="/images/iconchatbot.jpeg" alt="avatar-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nama dan Bubble Chat -->
|
||||||
|
<div class="col-9 w-auto">
|
||||||
|
<!-- Nama Bot -->
|
||||||
|
<p class="fw-bolder mb-1">Neng Bedas</p>
|
||||||
|
|
||||||
|
<!-- Bubble Chat -->
|
||||||
|
<div class="bot-response p-2 bg-light rounded mb-2 d-inline-block">
|
||||||
|
<p class="mb-0">Halo! Ada yang bisa saya bantu?</p>
|
||||||
|
|
||||||
|
<!-- Waktu (Tetap di Dalam Bubble Chat) -->
|
||||||
|
<div class="sending-message-time text-end mt-1">
|
||||||
|
<p class="text-muted small mb-0">Now</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Input & Button (Selalu di Bawah) -->
|
||||||
|
<div class="row mt-auto">
|
||||||
|
<div class="col-xl-12 d-flex align-items-end gap-1">
|
||||||
|
<textarea class="form-control" id="user-message"></textarea>
|
||||||
|
<button id="send" class="btn btn-primary btn-lg h-100 d-flex align-items-center">
|
||||||
|
<i class='bx bx-send'></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
@vite(['resources/js/chatbot-pimpinan/index.js'])
|
||||||
|
@endsection
|
||||||
@@ -30,59 +30,66 @@
|
|||||||
@include('layouts.partials/page-title', ['title' => 'Chatbot', 'subtitle' => 'Chatbot'])
|
@include('layouts.partials/page-title', ['title' => 'Chatbot', 'subtitle' => 'Chatbot'])
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
|
||||||
<ul class="nav nav-tabs nav-justified">
|
<ul class="nav nav-tabs nav-justified">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="count-retribusi" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
|
<button id="count-retribusi" data-bs-toggle="tab" aria-expanded="false" class="nav-link active">
|
||||||
<span class="d-block d-sm-none"><i class="bx bx-home"></i></span>
|
<span class="d-block d-sm-none"><i class="bx bx-home"></i></span>
|
||||||
<span class="d-none d-sm-block">Perhitungan Retribusi</span>
|
<span class="d-none d-sm-block fs-4">Perhitungan Retribusi</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="document-validation" data-bs-toggle="tab" aria-expanded="true"
|
<button id="document-validation" data-bs-toggle="tab" aria-expanded="true" class="nav-link">
|
||||||
class="nav-link active">
|
|
||||||
<span class="d-block d-sm-none"><i class="bx bx-user"></i></span>
|
<span class="d-block d-sm-none"><i class="bx bx-user"></i></span>
|
||||||
<span class="d-none d-sm-block">Validasi Dokumen PBG</span>
|
<span class="d-none d-sm-block fs-4">Validasi Dokumen PBG</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="data-information" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
|
<button id="data-information" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
|
||||||
<span class="d-block d-sm-none"><i class="bx bx-envelope"></i></span>
|
<span class="d-block d-sm-none"><i class="bx bx-envelope"></i></span>
|
||||||
<span class="d-none d-sm-block">Pengumpulan Data PBG</span>
|
<span class="d-none d-sm-block fs-4">Pengumpulan Data PBG</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
{{-- <div class="card-header">
|
||||||
|
</div> --}}
|
||||||
<div class="card-body d-flex flex-column" style="height: 700px;">
|
<div class="card-body d-flex flex-column" style="height: 700px;">
|
||||||
<!-- Conversation Area -->
|
<!-- Conversation Area -->
|
||||||
|
|
||||||
<!-- Bot Response -->
|
<!-- Bot Response -->
|
||||||
<div class="row flex-grow overflow-auto">
|
<div class="row flex-grow overflow-auto align-items-start">
|
||||||
|
<!-- Avatar -->
|
||||||
|
<div class="col-auto alignpe-0">
|
||||||
|
<img class="rounded-circle" width="45" src="/images/iconchatbot.jpeg" alt="avatar-3">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nama dan Bubble Chat -->
|
||||||
<div class="col-9 w-auto">
|
<div class="col-9 w-auto">
|
||||||
<span class="d-flex align-items-center mb-1">
|
<!-- Nama Bot -->
|
||||||
<img class="rounded-circle" width="32" src="/images/iconchatbot.jpeg" alt="avatar-3">
|
<p class="fw-bolder mb-1">Neng Bedas</p>
|
||||||
</span>
|
|
||||||
<div class="bot-response p-2 bg-light rounded mb-2">
|
<!-- Bubble Chat -->
|
||||||
<p>Halo! Ada yang bisa saya bantu?</p>
|
<div class="bot-response p-2 bg-light rounded mb-2 d-inline-block">
|
||||||
|
<p class="mb-0">Halo! Ada yang bisa saya bantu?</p>
|
||||||
|
|
||||||
|
<!-- Waktu (Tetap di Dalam Bubble Chat) -->
|
||||||
|
<div class="sending-message-time text-end mt-1">
|
||||||
|
<p class="text-muted small mb-0">Now</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Input & Button (Selalu di Bawah) -->
|
<!-- Input & Button (Selalu di Bawah) -->
|
||||||
<div class="row mt-auto">
|
<div class="row mt-auto">
|
||||||
<div class="col-xl-12 d-flex align-items-end gap-1">
|
<div class="col-xl-12 d-flex align-items-end gap-1">
|
||||||
<textarea class="form-control" id="user-message"></textarea>
|
<textarea class="form-control" id="user-message"></textarea>
|
||||||
<button id="send" class="btn btn-primary btn-lg rounded-pill">
|
<button id="send" class="btn btn-primary btn-lg h-100 d-flex align-items-center">
|
||||||
<i class='bx bx-send'></i>
|
<i class='bx bx-send'></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{{-- <div class="col-xl-2 d-flex justify-content-end">
|
|
||||||
|
|
||||||
</div> --}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
@extends('layouts.vertical', ['subtitle' => 'Data'])
|
@extends('layouts.vertical', ['subtitle' => 'PDAM'])
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Tata Ruang'])
|
@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'PDAM'])
|
||||||
|
|
||||||
<x-toast-notification />
|
<x-toast-notification />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan'])
|
@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan'])
|
||||||
|
|
||||||
<div id="dashboard-fixed-wrapper" class="row">
|
<div id="dashboard-fixed-wrapper" class="row">
|
||||||
<div class="col-12">
|
<!-- <div class="col-12">
|
||||||
<h2 class="mt-3 ms-2 text-danger">
|
<h2 class="mt-3 ms-2 text-danger">
|
||||||
<span class="float-end fs-6 me-3 text-black d-block d-sm-inline text-end">Terakhir di update - {{$latest_created}}</span>
|
<span class="float-end fs-6 me-3 text-black d-block d-sm-inline text-end">Terakhir di update - {{$latest_created}}</span>
|
||||||
ANALISA BIG DATA PROSES PBG <br>
|
ANALISA BIG DATA PROSES PBG <br>
|
||||||
@@ -24,6 +24,18 @@
|
|||||||
<input type="text" class="form-control" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
|
<input type="text" class="form-control" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div> -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
|
||||||
|
<h2 class="text-danger m-0">
|
||||||
|
ANALISA BIG DATA PROSES PBG <br>
|
||||||
|
MELALUI APLIKASI SIBEDAS PBG
|
||||||
|
</h2>
|
||||||
|
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||||
|
<span class="fs-5">Terakhir di update - {{$latest_created}}</span>
|
||||||
|
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-dashboard-bigdata" placeholder="Filter Date" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="dashboard-fixed-container" class="row" style="width:1110px;height:770px;position:relative;margin:auto;">
|
<div id="dashboard-fixed-container" class="row" style="width:1110px;height:770px;position:relative;margin:auto;">
|
||||||
@component('components.circle', [
|
@component('components.circle', [
|
||||||
@@ -37,7 +49,7 @@
|
|||||||
@endcomponent
|
@endcomponent
|
||||||
|
|
||||||
@component('components.circle', [
|
@component('components.circle', [
|
||||||
'document_title' => 'Target PAD 2024',
|
'document_title' => 'Target PAD',
|
||||||
'document_color' => '#204f6b',
|
'document_color' => '#204f6b',
|
||||||
'document_type' => '',
|
'document_type' => '',
|
||||||
'document_id' => 'chart-target-pad',
|
'document_id' => 'chart-target-pad',
|
||||||
|
|||||||
@@ -9,23 +9,19 @@
|
|||||||
|
|
||||||
<div class="lack-of-potential-wrapper">
|
<div class="lack-of-potential-wrapper">
|
||||||
<div class="row" id="lack-of-potential-wrapper">
|
<div class="row" id="lack-of-potential-wrapper">
|
||||||
<div class="col-12">
|
<div class="d-flex justify-content-between align-items-center mt-3 ms-2">
|
||||||
<h3 class="mt-3 ms-2 text-danger">
|
<h2 class="text-danger m-0">
|
||||||
ANALISA BIG DATA MELALUI APLIKASI SIBEDAS PBG
|
ANALISA BIG DATA MELALUI APLIKASI SIBEDAS PBG
|
||||||
</h3>
|
</h2>
|
||||||
|
<div class="text-black text-end d-flex flex-column align-items-end me-3">
|
||||||
|
<input type="text" class="form-control mt-2" style="max-width: 125px;" id="datepicker-lack-of-potential" placeholder="Filter Date" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="row d-flex justify-content-end">
|
|
||||||
<div class="col-12 col-sm-6 col-md-3">
|
|
||||||
<div class="d-flex flex-sm-nowrap flex-wrap justify-content-end">
|
|
||||||
<input type="text" class="form-control me-3" style="max-width: 125px;" id="datepicker-lack-of-potential" placeholder="Filter Date" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
|
||||||
<div style="position: absolute; top: 200px; left: 50px;">
|
<div style="position: absolute; top: 200px; left: 50px;">
|
||||||
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" />
|
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" visible_data="true" data_id="restoran-count" data_count="0" />
|
||||||
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
|
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
|
||||||
<x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
<x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" />
|
||||||
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
|
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
|
||||||
@@ -56,11 +52,11 @@
|
|||||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
|
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
|
||||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
|
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
|
||||||
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
|
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
|
||||||
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" />
|
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" visible_data="true" data_id="villa-count" data_count="0" />
|
||||||
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
|
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
|
||||||
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
|
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
|
||||||
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
|
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
|
||||||
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" />
|
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" visible_data="true" data_id="pariwisata-count" data_count="0" />
|
||||||
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
|
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
|
||||||
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
|
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
|
||||||
</div>
|
</div>
|
||||||
@@ -79,9 +75,7 @@
|
|||||||
'style' => 'margin-left:180px;top:-20px;'
|
'style' => 'margin-left:180px;top:-20px;'
|
||||||
])
|
])
|
||||||
@endcomponent
|
@endcomponent
|
||||||
|
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" visible_data="true" data_id="tata-ruang-count" data_count="0" />
|
||||||
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" />
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="position: absolute; top: 310px; left: 1150px;">
|
<div style="position: absolute; top: 310px; left: 1150px;">
|
||||||
@@ -89,7 +83,6 @@
|
|||||||
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
|
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
|
||||||
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
|
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
|
||||||
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
|
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
|
<x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
|
||||||
@@ -109,7 +102,7 @@
|
|||||||
|
|
||||||
<div style="position: absolute; top: 50px; left: 1100px;">
|
<div style="position: absolute; top: 50px; left: 1100px;">
|
||||||
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
|
||||||
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" />
|
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" visible_data="true" data_id="tata-ruang-usaha-count" data_count="0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,32 +3,57 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* .floating-icon {
|
/* .floating-icon {
|
||||||
position: fixed;
|
|
||||||
right: 30px;
|
|
||||||
bottom: 70px;
|
|
||||||
background: white;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 1000;
|
|
||||||
} */
|
|
||||||
.floating-icon {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 40px;
|
right: 40px;
|
||||||
bottom: 100px;
|
bottom: 100px;
|
||||||
width: 70px; /* Sesuaikan ukuran */
|
width: 70px;
|
||||||
height: 70px; /* Sesuaikan ukuran */
|
height: 70px;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
background-image: url('/images/iconchatbot.jpeg'); /* Path ke gambar */
|
background-image: url('/images/iconchatbot.jpeg');
|
||||||
background-size: cover; /* Agar gambar menyesuaikan */
|
background-size: cover;
|
||||||
background-position: center; /* Memusatkan gambar */
|
background-position: center;
|
||||||
|
} */
|
||||||
|
|
||||||
|
|
||||||
|
.floating-icon {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1000;
|
||||||
|
background-image: url('/images/iconchatbot.jpeg');
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-icon:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-icon.animate {
|
||||||
|
animation: bounce 1s infinite;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
@@ -61,7 +86,7 @@
|
|||||||
{{-- <div class="floating-icon">
|
{{-- <div class="floating-icon">
|
||||||
|
|
||||||
</div> --}}
|
</div> --}}
|
||||||
@if (!Request::is('chatbot'))
|
@if (!Request::is('chatbot') && !Request::is('main-chatbot'))
|
||||||
<a href="{{ route('chatbot.index') }}" class="floating-icon">
|
<a href="{{ route('chatbot.index') }}" class="floating-icon">
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,45 +1,41 @@
|
|||||||
@extends('layouts.vertical', ['subtitle' => 'Google Maps'])
|
@extends('layouts.vertical', ['subtitle' => 'Google Maps'])
|
||||||
|
|
||||||
@section('css')
|
@section('css')
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
<style>
|
||||||
|
.loading-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5); /* Latar belakang gelap transparan */
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
background: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@include('layouts.partials/page-title', ['title' => 'Maps', 'subtitle' => 'Google Maps'])
|
@include('layouts.partials.page-title', ['title' => 'Maps', 'subtitle' => 'Google Maps'])
|
||||||
|
<!-- Elemen loading -->
|
||||||
|
<div id="loading" class="loading-overlay">
|
||||||
|
<div class="loading-text">Loading data...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Peta -->
|
||||||
<div id="map" style="width: 100%; height: 90vh;"></div>
|
<div id="map" style="width: 100%; height: 90vh;"></div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
@vite(['resources/js/maps/maps-kml.js'])
|
||||||
<script src="https://unpkg.com/togeojson@0.16.0"></script>
|
|
||||||
<script src="https://unpkg.com/leaflet-omnivore@0.3.4/leaflet-omnivore.min.js"></script>
|
|
||||||
<script src="https://unpkg.com/leaflet-kml/L.KML.js"></script>
|
|
||||||
<script>
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
|
||||||
var map = L.map('map').setView([-6.9175, 107.6191], 10); // Jakarta
|
|
||||||
|
|
||||||
// Tambahkan peta dasar dari OpenStreetMap
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
||||||
attribution: '© OpenStreetMap contributors'
|
|
||||||
}).addTo(map);
|
|
||||||
|
|
||||||
// Muat file KMZ
|
|
||||||
omnivore.kml("{{ asset('storage/maps/rencanapolaruang_rtrw_2024_2044__rencana_pola_ruang.kml') }}")
|
|
||||||
.on('ready', function () {
|
|
||||||
try {
|
|
||||||
var bounds = this.getBounds();
|
|
||||||
if (bounds.isValid()) {
|
|
||||||
map.fitBounds(bounds);
|
|
||||||
} else {
|
|
||||||
console.warn("Bounds tidak valid, gunakan fallback.");
|
|
||||||
map.setView([-6.9175, 107.6191], 10); // Default ke Jakarta
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error setting bounds:", error);
|
|
||||||
map.setView([-6.1751, 106.8650], 10);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.addTo(map);
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endsection
|
@endsection
|
||||||
@@ -92,6 +92,11 @@
|
|||||||
<span class="d-none d-sm-block">PBG Task Prasarana</span>
|
<span class="d-none d-sm-block">PBG Task Prasarana</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="#pbgTaskAssignments" data-bs-toggle="tab" aria-expanded="false" class="nav-link">
|
||||||
|
<span class="d-none d-sm-block">Penugasan</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -246,6 +251,40 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="pbgTaskAssignments">
|
||||||
|
@if ($data->taskAssignments && $data->taskAssignments->isNotEmpty())
|
||||||
|
@foreach ($data->taskAssignments as $task_assignment)
|
||||||
|
<div class="border p-3 rounded shadow-sm col-md-4">
|
||||||
|
<div class="mb-3">
|
||||||
|
<dt>Nama</dt>
|
||||||
|
<dd>{{$task_assignment->name}}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<dt>Email</dt>
|
||||||
|
<dd>{{$task_assignment->email}}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<dt>Nomor Telepon</dt>
|
||||||
|
<dd>{{$task_assignment->phone_number}}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<dt>Keahlian</dt>
|
||||||
|
<dd>{{$task_assignment->expertise}}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<dt>Status</dt>
|
||||||
|
<dd>{{$task_assignment->status_name}}</dd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@else
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
Data Not Available
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,14 +7,16 @@
|
|||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Syncronize'])
|
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Syncronize'])
|
||||||
|
<x-toast-notification />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card w-100">
|
<div class="card w-100">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex flex-wrap justify-content-end gap-2">
|
<div class="d-flex flex-wrap justify-content-end gap-2">
|
||||||
<button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit-google-sheet">Sync Google Sheet</button>
|
<button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit">
|
||||||
<button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit">Sync SIMBG</button>
|
<span id="spinner" class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
|
||||||
|
Sync SIMBG
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div id="table-import-datasources"></div>
|
<div id="table-import-datasources"></div>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use Illuminate\Support\Facades\Route;
|
|||||||
|
|
||||||
Route::post('/login', [UsersController::class, 'login'])->name('api.user.login');
|
Route::post('/login', [UsersController::class, 'login'])->name('api.user.login');
|
||||||
Route::post('/generate-text', [ChatbotController::class, 'generateText']);
|
Route::post('/generate-text', [ChatbotController::class, 'generateText']);
|
||||||
|
Route::post('/main-generate-text', [ChatbotController::class, 'mainGenerateText']);
|
||||||
Route::group(['middleware' => 'auth:sanctum'], function (){
|
Route::group(['middleware' => 'auth:sanctum'], function (){
|
||||||
// users
|
// users
|
||||||
Route::controller(UsersController::class)->group(function(){
|
Route::controller(UsersController::class)->group(function(){
|
||||||
@@ -101,6 +102,7 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
|||||||
Route::get('/get-user-token', [SyncronizeController::class, 'getUserToken'])->name('api.task.token');
|
Route::get('/get-user-token', [SyncronizeController::class, 'getUserToken'])->name('api.task.token');
|
||||||
Route::get('/get-index-integration-retribution/{uuid}', [SyncronizeController::class, 'syncIndexIntegration'])->name('api.task.inntegration');
|
Route::get('/get-index-integration-retribution/{uuid}', [SyncronizeController::class, 'syncIndexIntegration'])->name('api.task.inntegration');
|
||||||
Route::get('/sync-task-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit');
|
Route::get('/sync-task-submit/{uuid}', [SyncronizeController::class, 'syncTaskDetailSubmit'])->name('api.task.submit');
|
||||||
|
Route::get('/sync-task-assignments/{uuid}', [SyncronizeController::class, 'syncTaskAssignments'])->name('api.task.assignments');
|
||||||
|
|
||||||
// menus api
|
// menus api
|
||||||
Route::controller(MenusController::class)->group(function (){
|
Route::controller(MenusController::class)->group(function (){
|
||||||
@@ -134,5 +136,8 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
|||||||
Route::get('/dashboard-potential-count', [LackOfPotentialController::class, 'count_lack_of_potential'])->name('api.count-dashboard-potential');
|
Route::get('/dashboard-potential-count', [LackOfPotentialController::class, 'count_lack_of_potential'])->name('api.count-dashboard-potential');
|
||||||
|
|
||||||
// big data resume
|
// big data resume
|
||||||
Route::get('/bigdata-resume', [BigDataResumeController::class, 'index'])->name('api.bigdata-resume');
|
Route::controller(BigDataResumeController::class)->group(function (){
|
||||||
|
Route::get('/bigdata-resume', 'index')->name('api.bigdata-resume');
|
||||||
|
Route::get('/bigdata-report', 'bigdata_report')->name('api.bigdata-report');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -8,4 +8,4 @@ Artisan::command('inspire', function () {
|
|||||||
$this->comment(Inspiring::quote());
|
$this->comment(Inspiring::quote());
|
||||||
})->purpose('Display an inspiring quote')->hourly();
|
})->purpose('Display an inspiring quote')->hourly();
|
||||||
|
|
||||||
Schedule::command("app:execute-scraping")->daily();
|
Schedule::command("app:execute-scraping")->dailyAt("00:00");
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\BigdataResumesController;
|
||||||
use App\Http\Controllers\BusinessOrIndustriesController;
|
use App\Http\Controllers\BusinessOrIndustriesController;
|
||||||
use App\Http\Controllers\CustomersController;
|
use App\Http\Controllers\CustomersController;
|
||||||
use App\Http\Controllers\Dashboards\LackOfPotentialController;
|
use App\Http\Controllers\Dashboards\LackOfPotentialController;
|
||||||
@@ -20,6 +21,7 @@ use App\Http\Controllers\Data\SpatialPlanningController;
|
|||||||
use App\Http\Controllers\Report\ReportTourismController;
|
use App\Http\Controllers\Report\ReportTourismController;
|
||||||
use App\Http\Controllers\SpatialPlanningsController;
|
use App\Http\Controllers\SpatialPlanningsController;
|
||||||
use App\Http\Controllers\Chatbot\ChatbotController;
|
use App\Http\Controllers\Chatbot\ChatbotController;
|
||||||
|
use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
require __DIR__ . '/auth.php';
|
require __DIR__ . '/auth.php';
|
||||||
@@ -62,6 +64,9 @@ Route::group(['middleware' => 'auth'], function(){
|
|||||||
// chatbot
|
// chatbot
|
||||||
Route::resource('/chatbot', ChatbotController::class);
|
Route::resource('/chatbot', ChatbotController::class);
|
||||||
|
|
||||||
|
// chatbot - pimpinan
|
||||||
|
Route::resource('/main-chatbot', ChatbotPimpinanController::class);
|
||||||
|
|
||||||
// roles
|
// roles
|
||||||
Route::resource('/roles', RolesController::class);
|
Route::resource('/roles', RolesController::class);
|
||||||
Route::group(['prefix' => '/roles'], function (){
|
Route::group(['prefix' => '/roles'], function (){
|
||||||
@@ -72,30 +77,30 @@ Route::group(['middleware' => 'auth'], function(){
|
|||||||
// data
|
// data
|
||||||
Route::group(['prefix' => '/data'], function(){
|
Route::group(['prefix' => '/data'], function(){
|
||||||
// Resource route, kecuali create karena dibuat terpisah
|
// Resource route, kecuali create karena dibuat terpisah
|
||||||
Route::resource('/advertisements', AdvertisementController::class)->except(['create', 'show']);
|
Route::resource('/web-advertisements', AdvertisementController::class)->except(['create', 'show']);
|
||||||
|
|
||||||
// Rute khusus untuk create dan bulk-create
|
// Rute khusus untuk create dan bulk-create
|
||||||
Route::get('/advertisements/create', [AdvertisementController::class, 'create'])->name('advertisements.create');
|
Route::get('/advertisements/create', [AdvertisementController::class, 'create'])->name('advertisements.create');
|
||||||
Route::get('/advertisements/bulk-create', [AdvertisementController::class, 'bulkCreate'])->name('advertisements.bulk-create');
|
Route::get('/advertisements/bulk-create', [AdvertisementController::class, 'bulkCreate'])->name('advertisements.bulk-create');
|
||||||
|
|
||||||
// Resource route, kecuali create karena dibuat terpisah
|
// Resource route, kecuali create karena dibuat terpisah
|
||||||
Route::resource('/umkm', UmkmController::class)->except(['create', 'show']);
|
Route::resource('/web-umkm', UmkmController::class)->except(['create', 'show']);
|
||||||
|
|
||||||
// Rute khusus untuk create dan bulk-create
|
// Rute khusus untuk create dan bulk-create
|
||||||
Route::get('/umkm/create', [UmkmController::class, 'create'])->name('umkm.create');
|
Route::get('/umkm/create', [UmkmController::class, 'create'])->name('umkm.create');
|
||||||
Route::get('/umkm/bulk-create', [UmkmController::class, 'bulkCreate'])->name('umkm.bulk-create');
|
Route::get('/umkm/bulk-create', [UmkmController::class, 'bulkCreate'])->name('umkm.bulk-create');
|
||||||
|
|
||||||
// Resource route, kecuali create karena dibuat terpisah
|
// Resource route, kecuali create karena dibuat terpisah
|
||||||
Route::resource('/tourisms', TourismController::class)->except(['create', 'show']);
|
Route::resource('/web-tourisms', TourismController::class)->except(['create', 'show']);
|
||||||
// Rute khusus untuk create dan bulk-create
|
// Rute khusus untuk create dan bulk-create
|
||||||
Route::get('/tourisms/create', [TourismController::class, 'create'])->name('tourisms.create');
|
Route::get('/tourisms/create', [TourismController::class, 'create'])->name('tourisms.create');
|
||||||
Route::get('/tourisms/bulk-create', [TourismController::class, 'bulkCreate'])->name('tourisms.bulk-create');
|
Route::get('/tourisms/bulk-create', [TourismController::class, 'bulkCreate'])->name('tourisms.bulk-create');
|
||||||
|
|
||||||
// Resource route, kecuali create karena dibuat terpisah
|
// Resource route, kecuali create karena dibuat terpisah
|
||||||
Route::resource('/spatial-plannings', SpatialPlanningController::class)->except(['create', 'show']);
|
Route::resource('/web-spatial-plannings', SpatialPlanningController::class)->except(['create', 'show']);
|
||||||
// Rute khusus untuk create dan bulk-create
|
// Rute khusus untuk create dan bulk-create
|
||||||
Route::get('/spatial-plannings/create', [SpatialPlanningController::class, 'create'])->name('tourisms.create');
|
Route::get('/spatial-plannings/create', [SpatialPlanningController::class, 'create'])->name('spatial-plannings.create');
|
||||||
Route::get('/spatial-plannings/bulk-create', [SpatialPlanningController::class, 'bulkCreate'])->name('tourisms.bulk-create');
|
Route::get('/spatial-plannings/bulk-create', [SpatialPlanningController::class, 'bulkCreate'])->name('spatial-plannings.bulk-create');
|
||||||
|
|
||||||
|
|
||||||
Route::resource('/business-industries',BusinessOrIndustriesController::class);
|
Route::resource('/business-industries',BusinessOrIndustriesController::class);
|
||||||
@@ -112,5 +117,9 @@ Route::group(['middleware' => 'auth'], function(){
|
|||||||
Route::group(['prefix' => '/report'], function(){
|
Route::group(['prefix' => '/report'], function(){
|
||||||
// Resource route, kecuali create karena dibuat terpisah
|
// Resource route, kecuali create karena dibuat terpisah
|
||||||
Route::resource('/tourisms-report', ReportTourismController::class);
|
Route::resource('/tourisms-report', ReportTourismController::class);
|
||||||
|
|
||||||
|
Route::controller(BigdataResumesController::class)->group(function (){
|
||||||
|
Route::get('/bigdata-resumes', 'index')->name('bigdata-resumes');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -3,10 +3,6 @@ import laravel from "laravel-vite-plugin";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
|
||||||
outDir: "public/build",
|
|
||||||
manifest: true, // Menghasilkan manifest.json untuk Laravel
|
|
||||||
},
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve(__dirname, "resources/js"),
|
"@": path.resolve(__dirname, "resources/js"),
|
||||||
@@ -98,7 +94,13 @@ export default defineConfig({
|
|||||||
"resources/js/customers/index.js",
|
"resources/js/customers/index.js",
|
||||||
"resources/js/customers/create.js",
|
"resources/js/customers/create.js",
|
||||||
"resources/js/customers/edit.js",
|
"resources/js/customers/edit.js",
|
||||||
"resources/js/dashboards/pbg.js"
|
"resources/js/dashboards/pbg.js",
|
||||||
|
// maps
|
||||||
|
"resources/js/maps/maps-kml.js",
|
||||||
|
// laporan pimpinan
|
||||||
|
"resources/js/bigdata-resumes/index.js",
|
||||||
|
"resources/js/chatbot/index.js",
|
||||||
|
"resources/js/chatbot-pimpinan/index.js",
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user