diff --git a/.env.local.backup b/.env.local.backup new file mode 100644 index 0000000..9527dc3 --- /dev/null +++ b/.env.local.backup @@ -0,0 +1,76 @@ +APP_NAME=SIBEDAS-PBG +APP_ENV=local +APP_KEY=base64:xqCpwixWKqgu1Ca22gFizoOt44p7h+cgTOKuhS/P0Jw= +APP_DEBUG=true +APP_TIMEZONE=Asia/Jakarta +APP_URL=http://localhost:8000 + +API_URL=http://localhost:8000 + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mariadb +DB_HOST=db +DB_PORT=3306 +DB_DATABASE=sibedas_db +DB_USERNAME=root +DB_PASSWORD=root + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +SIMBG_HOST="https://simbg.pu.go.id/" +SIMBG_EMAIL="dputr@bandungkab.go.id" +SIMBG_PASSWORD="Simbg123" + +API_KEY_GOOGLE="AIzaSyBxfEShFkKmykkc7RJR3lVzkQ_xGHK3qr0" +SPREAD_SHEET_ID="1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY" +OPENAI_API_KEY="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" \ No newline at end of file diff --git a/BUILD_GUIDE.md b/BUILD_GUIDE.md deleted file mode 100644 index 2e2eb8b..0000000 --- a/BUILD_GUIDE.md +++ /dev/null @@ -1,180 +0,0 @@ -# π Panduan Build untuk Production - -## π Masalah yang Diselesaikan - -Aplikasi ini memiliki **banyak file JS dan CSS** yang harus dibangun semuanya karena: -- Setiap halaman memerlukan file JS/CSS yang spesifik -- Jika ada file yang hilang, halaman akan error (404 Not Found) -- Server dengan RAM terbatas sering mengalami "killed" saat build - -## π― Solusi yang Tersedia - -### 1. **Build di Local (RECOMMENDED) π** -```bash -# Build di local computer Anda -npm run build:local - -# Upload ke server -./deploy.sh username server.com /path/to/project -``` - -**Kelebihan:** -- β Tidak ada batasan memory -- β Build lebih cepat -- β Semua file terjamin ter-build - -### 2. **Build di Server dengan Optimasi** -```bash -# Option A: Build dengan memory optimization -npm run build:prod - -# Option B: Build dengan retry mechanism -npm run build:chunked - -# Option C: Build manual dengan script -./build-production.sh -``` - -### 3. **Setup Server untuk Build** -```bash -# Setup swap memory dan optimasi (run dengan sudo) -sudo ./server-setup.sh - -# Kemudian build -npm run build:prod -``` - -## π File yang Akan Dibangun - -Semua file ini **WAJIB** ada karena dibutuhkan oleh halaman-halaman aplikasi: - -### CSS Files: -- `resources/scss/style.scss` - Main stylesheet -- `resources/scss/icons.scss` - Icons -- `resources/scss/components/_*.scss` - Components -- `resources/scss/dashboards/_*.scss` - Dashboard styles -- `resources/scss/pages/quick-search/*.scss` - Quick search pages -- Third-party CSS (Quill, Flatpickr, GridJS, dll) - -### JS Files: -- **Core:** `app.js`, `config.js`, `dashboard.js` -- **Pages:** `chart.js`, `form-*.js`, `table-*.js`, `maps-*.js` -- **Data:** `data-advertisements.js`, `data-umkm.js`, `data-tourisms.js` -- **Settings:** `syncronize.js`, `general-settings.js` -- **Dashboards:** `bigdata.js`, `pbg.js`, `potentials/*.js` -- **Users & Roles:** `users/*.js`, `roles/*.js`, `menus/*.js` -- **Reports:** `growth-report.js`, `tourisms/index.js` -- **Dan semua file lainnya sesuai kebutuhan halaman** - -## βοΈ Konfigurasi Build - -### Memory Optimization -```javascript -// vite.config.production.js -export default defineConfig({ - build: { - rollupOptions: { - output: { - manualChunks: { - vendor: ['bootstrap', 'moment', 'axios'], - charts: ['apexcharts'], - maps: ['leaflet', 'jsvectormap', 'gmaps'], - ui: ['sweetalert2', 'flatpickr', 'quill'] - } - }, - maxParallelFileOps: 1 // Untuk server dengan RAM terbatas - } - } -}) -``` - -### Memory Limits -```bash -# Untuk server dengan RAM 2GB+ -NODE_OPTIONS="--max-old-space-size=2048" - -# Untuk server dengan RAM 1GB -NODE_OPTIONS="--max-old-space-size=1024" -``` - -## π§ Troubleshooting - -### Build Terkill (Killed) -```bash -# Solusi 1: Build di local -npm run build:local -./deploy.sh - -# Solusi 2: Tambah swap memory -sudo ./server-setup.sh - -# Solusi 3: Gunakan build chunked -npm run build:chunked -``` - -### File JS/CSS Tidak Ditemukan -```bash -# Pastikan semua file ada di vite.config.production.js -# Jangan hapus file apapun dari daftar input! - -# Verifikasi build -find public/build -name "*.js" | wc -l -find public/build -name "*.css" | wc -l -``` - -### Server Kehabisan Memory -```bash -# Check memory usage -free -h - -# Add swap file -sudo fallocate -l 2G /swapfile -sudo chmod 600 /swapfile -sudo mkswap /swapfile -sudo swapon /swapfile -``` - -## π¦ Deployment Flow - -### Local Build β Server Deploy -```bash -# 1. Di local -npm ci -npm run build:local - -# 2. Edit deploy.sh dengan info server -# 3. Deploy -./deploy.sh username server.com /path/to/project -``` - -### Server Build -```bash -# 1. Setup server -sudo ./server-setup.sh - -# 2. Install dependencies -npm ci --production=false - -# 3. Build -npm run build:chunked - -# 4. Optimize Laravel -php artisan config:cache -php artisan route:cache -php artisan view:cache -``` - -## β οΈ Catatan Penting - -1. **JANGAN HAPUS FILE APAPUN** dari `vite.config.production.js` -2. **SEMUA FILE WAJIB ADA** karena dibutuhkan oleh halaman-halaman aplikasi -3. **BUILD DI LOCAL** adalah solusi terbaik untuk server dengan RAM terbatas -4. **BACKUP** selalu sebelum deploy ke production - -## π Success Indicators - -Build berhasil jika: -- β File `public/build/manifest.json` ada -- β Folder `public/build/assets/` berisi banyak file JS/CSS -- β Tidak ada error 404 saat mengakses halaman -- β Semua halaman dapat memuat JS/CSS yang dibutuhkan \ No newline at end of file diff --git a/app/Console/Commands/ScrapingLeaderData.php b/app/Console/Commands/ScrapingLeaderData.php new file mode 100644 index 0000000..04f8bc9 --- /dev/null +++ b/app/Console/Commands/ScrapingLeaderData.php @@ -0,0 +1,33 @@ +sync_leader_data(); + $this->info('Leader data synced successfully'); + } +} diff --git a/app/Http/Controllers/Dashboards/BigDataController.php b/app/Http/Controllers/Dashboards/BigDataController.php index 780c61d..42e8f14 100644 --- a/app/Http/Controllers/Dashboards/BigDataController.php +++ b/app/Http/Controllers/Dashboards/BigDataController.php @@ -21,4 +21,13 @@ class BigDataController extends Controller { return view('dashboards.pbg'); } + + public function leader() + { + $latest_import_datasource = ImportDatasource::latest()->first(); + $latest_created = $latest_import_datasource ? + $latest_import_datasource->created_at->format("j F Y H:i:s") : null; + $menus = Menu::all(); + return view('dashboards.leader', compact('latest_created', 'menus')); + } } diff --git a/app/Services/ServiceGoogleSheet.php b/app/Services/ServiceGoogleSheet.php index 6a046be..dcf1258 100644 --- a/app/Services/ServiceGoogleSheet.php +++ b/app/Services/ServiceGoogleSheet.php @@ -1,14 +1,16 @@ 'Processing leader data', + 'status' => 'processing', + 'start_time' => now(), + 'failed_uuid' => null + ]); + try { + $sections = [ + 'TARGET_PAD' => "TARGET PAD 2024", + 'KEKURANGAN_POTENSI' => "DEVIASI TARGET DENGAN POTENSI TOTAL BERKAS", + 'TOTAL_POTENSI_BERKAS' => "β’TOTAL BERKAS 2025", + 'BELUM_TERVERIFIKASI' => "β’BERKAS AKTUAL BELUM TERVERIFIKASI (POTENSI):", + 'TERVERIFIKASI' => "β’BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2025:", + 'NON_USAHA' => "β’NON USAHA: HUNIAN, SOSBUD, KEAGAMAAN", + 'USAHA' => "β’USAHA: USAHA, CAMPURAN, KOLEKTIF, PRASARANA" + ]; + + $result = []; + + foreach ($sections as $key => $identifier) { + $values = $this->get_values_from_section(2, $identifier, [10, 11]); + + if (!empty($values)) { + $result[$key] = [ + 'identifier' => $identifier, + 'total' => $values[0] ?? null, // index 0 untuk total/jumlah + 'nominal' => $values[1] ?? null // index 1 untuk nominal + ]; + } + } + + Log::info("Leader data synced", ['result' => $result]); + + BigdataResume::create([ + 'import_datasource_id' => $import_datasource->id, + 'year' => now()->year, + // USAHA + 'business_count' => $this->convertToInteger($result['USAHA']['total'] ?? null) ?? 0, + 'business_sum' => $this->convertToDecimal($result['USAHA']['nominal'] ?? null) ?? 0, + // NON USAHA + 'non_business_count' => $this->convertToInteger($result['NON_USAHA']['total'] ?? null) ?? 0, + 'non_business_sum' => $this->convertToDecimal($result['NON_USAHA']['nominal'] ?? null) ?? 0, + // TERVERIFIKASI + 'verified_count' => $this->convertToInteger($result['TERVERIFIKASI']['total'] ?? null) ?? 0, + 'verified_sum' => $this->convertToDecimal($result['TERVERIFIKASI']['nominal'] ?? null) ?? 0, + // BELUM TERVERIFIKASI + 'non_verified_count' => $this->convertToInteger($result['BELUM_TERVERIFIKASI']['total'] ?? null) ?? 0, + 'non_verified_sum' => $this->convertToDecimal($result['BELUM_TERVERIFIKASI']['nominal'] ?? null) ?? 0, + // TOTAL POTENSI BERKAS + 'potention_count' => $this->convertToInteger($result['TOTAL_POTENSI_BERKAS']['total'] ?? null) ?? 0, + 'potention_sum' => $this->convertToDecimal($result['TOTAL_POTENSI_BERKAS']['nominal'] ?? null) ?? 0, + ]); + + $import_datasource->update([ + 'status' => 'success', + 'response_body' => json_encode($result), + 'message' => 'Leader data synced', + 'finish_time' => now() + ]); + return $result; + } catch (\Exception $e) { + Log::error("Error syncing leader data", ['error' => $e->getMessage()]); + $import_datasource->update([ + 'status' => 'failed', + 'message' => 'Leader data sync failed', + 'finish_time' => now() + ]); + throw $e; + } + } + public function get_big_resume_data(){ try { $sheet_big_data = $this->get_data_by_sheet(); @@ -261,9 +335,71 @@ class ServiceGoogleSheet return!empty($values)? $values : []; } + /** + * Get specific values from a row that contains a specific text/section identifier + * @param int $no_sheet Sheet number (0-based) + * @param string $section_identifier Text to search for in the row + * @param array $column_indices Array of column indices to extract values from + * @return array Array of values from specified columns, or empty array if section not found + */ + private function get_values_from_section($no_sheet = 1, $section_identifier, $column_indices = []) { + try { + $sheet_data = $this->get_data_by_sheet($no_sheet); + + if (empty($sheet_data)) { + Log::warning("No data found in sheet", ['sheet' => $no_sheet]); + return []; + } + + // Search for the row containing the section identifier + $target_row = null; + foreach ($sheet_data as $row_index => $row) { + if (is_array($row)) { + foreach ($row as $cell) { + if (is_string($cell) && strpos($cell, $section_identifier) !== false) { + $target_row = $row; + break 2; // Break out of both loops + } + } + } + } + + if ($target_row === null) { + Log::warning("Section not found", ['section_identifier' => $section_identifier]); + return []; + } + + // Extract values from specified column indices + $extracted_values = []; + foreach ($column_indices as $col_index) { + if (isset($target_row[$col_index])) { + $value = trim($target_row[$col_index]); + $extracted_values[] = $value !== '' ? $value : null; + } else { + $extracted_values[] = null; + } + } + + Log::info("Values extracted from section", [ + 'section_identifier' => $section_identifier, + 'column_indices' => $column_indices, + 'extracted_values' => $extracted_values + ]); + + return $extracted_values; + } catch (\Exception $e) { + Log::error("Error getting values from section", [ + 'error' => $e->getMessage(), + 'section_identifier' => $section_identifier, + 'sheet' => $no_sheet + ]); + return []; + } + } + private function convertToInteger($value) { - // Check if the value is an empty string, and return null if true - if (trim($value) === "") { + // Check if the value is null or empty string, and return null if true + if ($value === null || trim($value) === "") { return null; } diff --git a/build-chunked.sh b/build-chunked.sh deleted file mode 100644 index 69b4cad..0000000 --- a/build-chunked.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash - -# Build script bertahap untuk menghindari memory overload -# Tetap membangun SEMUA file yang diperlukan tapi secara bertahap - -echo "π¨ Starting chunked production build..." - -# Set memory limit -export NODE_OPTIONS="--max-old-space-size=1536" -export NODE_ENV=production - -# Clean previous build -echo "π§Ή Cleaning previous build..." -rm -rf public/build/* - -# Fungsi untuk build dengan retry -build_with_retry() { - local config_file=$1 - local attempt=1 - local max_attempts=3 - - while [ $attempt -le $max_attempts ]; do - echo "π Build attempt $attempt of $max_attempts..." - - if vite build --config $config_file; then - echo "β Build successful on attempt $attempt" - return 0 - else - echo "β Build failed on attempt $attempt" - if [ $attempt -lt $max_attempts ]; then - echo "β³ Waiting 5 seconds before retry..." - sleep 5 - # Clear memory caches - sync && echo 3 > /proc/sys/vm/drop_caches 2>/dev/null || true - fi - attempt=$((attempt + 1)) - fi - done - - return 1 -} - -echo "π¦ Building all assets with memory optimization..." - -# Try building with production config first -if build_with_retry "vite.config.production.js"; then - echo "π Build completed successfully!" - echo "π Build summary:" - du -sh public/build/* 2>/dev/null || echo "Build files created" - - # Count generated files - echo "π Generated files:" - find public/build -type f | wc -l | xargs echo "Total files:" - -else - echo "β Build failed after all attempts!" - echo "" - echo "π‘ Alternative solutions:" - echo " 1. β¨ Build locally: npm run build:local" - echo " 2. π§ Increase server memory/swap" - echo " 3. βοΈ Use GitHub Actions CI/CD" - echo " 4. π Use deployment services (Vercel, Netlify)" - echo "" - echo "π For local build and deploy:" - echo " npm run build:local" - echo " ./deploy.sh your-username your-server.com /path/to/project" - - exit 1 -fi - -echo "" -echo "π Verifying all essential files are built..." - -# Check for essential files -essential_files=( - "public/build/manifest.json" - "public/build/assets" -) - -missing_files=() -for file in "${essential_files[@]}"; do - if [ ! -e "$file" ]; then - missing_files+=("$file") - fi -done - -if [ ${#missing_files[@]} -eq 0 ]; then - echo "β All essential files are present!" -else - echo "β οΈ Missing files:" - printf ' %s\n' "${missing_files[@]}" -fi \ No newline at end of file diff --git a/build-production.sh b/build-production.sh deleted file mode 100644 index a5a6914..0000000 --- a/build-production.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# Production build script dengan optimasi memory -# Untuk server dengan resource terbatas - -echo "π¨ Starting production build with memory optimization..." - -# Set Node.js memory limit (adjust sesuai RAM server) -# Untuk server dengan banyak file, gunakan memory yang lebih besar -export NODE_OPTIONS="--max-old-space-size=2048 --max-semi-space-size=1024" - -# Use production vite config -export NODE_ENV=production - -# Clean previous build -echo "π§Ή Cleaning previous build..." -rm -rf public/build/* - -# Build with limited resources -echo "π¦ Building assets with memory optimization..." - -# Option 1: Build with custom config -vite build --config vite.config.production.js - -# Option 2: Alternative - build in chunks (uncomment if needed) -# echo "Building CSS files first..." -# vite build --config vite.config.production.js --mode css-only -# -# echo "Building JS files..." -# vite build --config vite.config.production.js --mode js-only - -if [ $? -eq 0 ]; then - echo "β Build completed successfully!" - echo "π Build summary:" - du -sh public/build/* -else - echo "β Build failed!" - echo "π‘ Try these solutions:" - echo " 1. Increase server memory" - echo " 2. Build locally and upload files" - echo " 3. Use swap memory (temporary solution)" - exit 1 -fi \ No newline at end of file diff --git a/build.zip b/build.zip deleted file mode 100644 index a928e1c..0000000 Binary files a/build.zip and /dev/null differ diff --git a/database/seeders/MenuSeeder.php b/database/seeders/MenuSeeder.php index e6baacd..b6c6406 100644 --- a/database/seeders/MenuSeeder.php +++ b/database/seeders/MenuSeeder.php @@ -30,7 +30,7 @@ class MenuSeeder extends Seeder "sort_order" => 2, "children" => [ [ - "name" => "Dashboard Pimpinan", + "name" => "Dashboard Pimpinan SIMBG", "url" => "dashboard.home", "icon" => null, "sort_order" => 1, @@ -67,6 +67,12 @@ class MenuSeeder extends Seeder "icon" => null, "sort_order" => 4, ], + [ + "name" => "Dashboard Pimpinan", + "url" => "dashboard.leader", + "icon" => null, + "sort_order" => 5, + ], ], ], [ diff --git a/debug-permissions.sh b/debug-permissions.sh deleted file mode 100644 index 930a975..0000000 --- a/debug-permissions.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash - -# Debug script untuk diagnosa permission issues -# Jalankan di server untuk check masalah permission - -PROJECT_PATH=${1:-"/var/www/pupr"} - -echo "π Debugging permissions for: $PROJECT_PATH" -echo "=================================================" - -# Check if path exists -if [ ! -d "$PROJECT_PATH" ]; then - echo "β Project path not found: $PROJECT_PATH" - exit 1 -fi - -cd "$PROJECT_PATH" - -echo "π Current User & Groups:" -echo "Current user: $(whoami)" -echo "Web server user: $(ps aux | grep -E 'apache|nginx|httpd' | grep -v grep | head -1 | awk '{print $1}')" -echo "" - -echo "π Directory Ownership & Permissions:" -echo "Project root:" -ls -la . | head -5 -echo "" -echo "Storage directory:" -ls -la storage/ -echo "" -echo "Storage/framework:" -ls -la storage/framework/ 2>/dev/null || echo "storage/framework/ not found" -echo "" -echo "Bootstrap/cache:" -ls -la bootstrap/cache/ 2>/dev/null || echo "bootstrap/cache/ not found" -echo "" - -echo "π Permission Analysis:" -# Check critical directories -directories=("storage" "storage/framework" "storage/framework/views" "storage/framework/cache" "storage/logs" "bootstrap/cache") - -for dir in "${directories[@]}"; do - if [ -d "$dir" ]; then - perm=$(stat -c "%a" "$dir") - owner=$(stat -c "%U:%G" "$dir") - echo "β $dir: $perm ($owner)" - else - echo "β $dir: NOT FOUND" - fi -done - -echo "" -echo "π§ͺ Write Test:" -# Test write permissions -if [ -w "storage/framework/" ]; then - echo "β storage/framework/ is writable" - # Try creating a test file - if touch storage/framework/test_write_$(date +%s).tmp 2>/dev/null; then - echo "β Can create files in storage/framework/" - rm -f storage/framework/test_write_*.tmp - else - echo "β Cannot create files in storage/framework/" - fi -else - echo "β storage/framework/ is NOT writable" -fi - -echo "" -echo "βοΈ SELinux Status (if applicable):" -if command -v getenforce &> /dev/null; then - echo "SELinux: $(getenforce)" - if [ "$(getenforce)" = "Enforcing" ]; then - echo "β οΈ SELinux is enforcing - may need additional configuration" - echo "Try: sudo setsebool -P httpd_unified on" - fi -else - echo "SELinux not installed" -fi - -echo "" -echo "π Recommended Actions:" -echo "1. Fix ownership: sudo chown -R www-data:www-data $PROJECT_PATH" -echo "2. Fix permissions: sudo chmod -R 775 $PROJECT_PATH/storage/" -echo "3. Clear cache: php artisan view:clear" -echo "4. If SELinux: sudo setsebool -P httpd_unified on" \ No newline at end of file diff --git a/deploy.sh b/deploy.sh deleted file mode 100644 index 95e365a..0000000 --- a/deploy.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -# Deploy script untuk build di local dan upload ke server -# Usage: ./deploy.sh [server-user] [server-host] [server-path] - -# Default values (ganti sesuai server Anda) -SERVER_USER=${1:-"root"} -SERVER_HOST=${2:-"157.245.48.15"} -SERVER_PATH=${3:-"/var/www/pupr"} - -echo "π Starting deployment process..." - -# 1. Clean previous build -echo "π§Ή Cleaning previous build..." -rm -rf public/build/* - -# 2. Install dependencies (jika belum) -echo "π¦ Installing dependencies..." -npm ci --production=false - -# 3. Build project -echo "π¨ Building project..." -npm run build - -# Check if build was successful -if [ $? -ne 0 ]; then - echo "β Build failed!" - exit 1 -fi - -echo "β Build completed successfully!" - -# 4. Upload build files to server -echo "π€ Uploading build files to server..." -rsync -avz --progress --delete public/build/ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/public/build/ - -# 5. Upload other necessary files (optional) -echo "π€ Uploading other files..." -rsync -avz --progress \ - --exclude='node_modules' \ - --exclude='.git' \ - --exclude='public/build' \ - --exclude='storage/logs/*' \ - --exclude='bootstrap/cache/*' \ - ./ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/ - -echo "π§ Fixing permissions on server..." -ssh $SERVER_USER@$SERVER_HOST "cd $SERVER_PATH && chmod +x fix-permissions.sh && sudo ./fix-permissions.sh $SERVER_PATH" - -echo "π¦ Installing composer dependencies on server..." -ssh $SERVER_USER@$SERVER_HOST "cd $SERVER_PATH && composer install --no-dev --optimize-autoloader" - -echo "π Deployment completed successfully!" -echo "" -echo "β Permissions have been automatically fixed!" -echo "β Composer dependencies installed!" -echo "β Laravel caches optimized!" -echo "" -echo "π Your application should now be accessible without permission errors!" \ No newline at end of file diff --git a/docker/supervisor/laravel-production.conf b/docker/supervisor/laravel-production.conf new file mode 100644 index 0000000..b290c89 --- /dev/null +++ b/docker/supervisor/laravel-production.conf @@ -0,0 +1,41 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:laravel-queue-worker] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/pupr/artisan queue:work --queue=default --timeout=82800 --tries=3 --memory=512 --sleep=3 +directory=/var/www/pupr +autostart=true +autorestart=true +numprocs=2 +redirect_stderr=true +stdout_logfile=/var/www/pupr/storage/logs/queue-worker.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=5 +stopasgroup=true +killasgroup=true +user=www-data +priority=10 + +[program:laravel-scheduler] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/pupr/artisan schedule:work +directory=/var/www/pupr +autostart=true +autorestart=true +numprocs=1 +redirect_stderr=true +stdout_logfile=/var/www/pupr/storage/logs/scheduler.log +stdout_logfile_maxbytes=10MB +stdout_logfile_backups=5 +stopasgroup=true +killasgroup=true +user=www-data +priority=20 + +[group:laravel] +programs=laravel-queue-worker,laravel-scheduler +priority=999 \ No newline at end of file diff --git a/docker/supervisor/supervisord.conf b/docker/supervisor/supervisord.conf index 7e6efd0..731e5b6 100644 --- a/docker/supervisor/supervisord.conf +++ b/docker/supervisor/supervisord.conf @@ -15,4 +15,17 @@ stdout_logfile=/var/www/storage/logs/worker.log stopasgroup=true killasgroup=true user=www-data -priority=10 \ No newline at end of file +priority=10 + +[program:laravel-scheduler] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/artisan schedule:work +autostart=true +autorestart=true +numprocs=1 +redirect_stderr=true +stdout_logfile=/var/www/storage/logs/scheduler.log +stopasgroup=true +killasgroup=true +user=www-data +priority=20 \ No newline at end of file diff --git a/fix-permissions.sh b/fix-permissions.sh deleted file mode 100644 index 3548583..0000000 --- a/fix-permissions.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash - -# Script untuk fix permissions Laravel setelah deployment -# Jalankan di server production - -PROJECT_PATH=${1:-"/var/www/pupr"} - -echo "π§ Fixing Laravel permissions for: $PROJECT_PATH" - -# Check if path exists -if [ ! -d "$PROJECT_PATH" ]; then - echo "β Project path not found: $PROJECT_PATH" - echo "Usage: ./fix-permissions.sh /path/to/your/project" - exit 1 -fi - -cd "$PROJECT_PATH" - -echo "π Setting correct ownership..." -# Set ownership to web server user -sudo chown -R www-data:www-data . - -echo "π Setting directory permissions..." -# Set directory permissions -sudo find . -type d -exec chmod 755 {} \; - -echo "π Setting file permissions..." -# Set file permissions -sudo find . -type f -exec chmod 644 {} \; - -echo "π Setting special permissions for Laravel..." -# Storage and cache directories need write permissions -sudo chmod -R 775 storage/ -sudo chmod -R 775 bootstrap/cache/ - -# Make sure these directories exist -sudo mkdir -p storage/framework/views -sudo mkdir -p storage/framework/cache -sudo mkdir -p storage/framework/sessions -sudo mkdir -p storage/logs -sudo mkdir -p storage/app/public - -# Set ownership for storage and bootstrap/cache -sudo chown -R www-data:www-data storage/ -sudo chown -R www-data:www-data bootstrap/cache/ - -echo "π Setting executable permissions for Artisan..." -sudo chmod +x artisan - -echo "π§Ή Clearing Laravel caches..." -# Clear all caches -php artisan config:clear -php artisan route:clear -php artisan view:clear -php artisan cache:clear - -# Recreate caches with correct permissions -php artisan config:cache -php artisan route:cache -php artisan view:cache - -echo "π Checking permissions..." -echo "Storage permissions:" -ls -la storage/ -echo "" -echo "Framework permissions:" -ls -la storage/framework/ -echo "" - -echo "β Permissions fixed successfully!" -echo "π‘ If you still get permission errors, also run:" -echo " sudo setsebool -P httpd_can_network_connect on" -echo " sudo setsebool -P httpd_unified on" \ No newline at end of file diff --git a/resources/js/dashboards/leader.js b/resources/js/dashboards/leader.js new file mode 100644 index 0000000..e898718 --- /dev/null +++ b/resources/js/dashboards/leader.js @@ -0,0 +1,378 @@ +import Big from "big.js"; +import GlobalConfig, { addThousandSeparators } from "../global-config.js"; +import InitDatePicker from "../utils/InitDatePicker.js"; + +class BigData { + async init() { + try { + new InitDatePicker( + "#datepicker-dashboard-bigdata", + this.handleChangeDate.bind(this) + ).init(); + + // Load initial data + this.updateData("latest"); + } catch (error) { + console.error("Error initializing data:", error); + } + } + + handleChangeDate(filterDate) { + if (!filterDate) return; + this.updateData(filterDate); + } + async updateData(filterDate) { + try { + this.resumeBigData = await this.getBigDataResume(filterDate); + + this.initChartTargetPAD(filterDate); + this.initChartUsaha(); + this.initChartNonUsaha(); + this.initChartTotalPotensi(); + this.initChartVerificationDocuments(); + this.initChartNonVerificationDocuments(); + this.initChartKekuranganPotensi(); + this.initChartRealisasiTerbitPBG(); + this.initChartMenungguKlikDPMPTSP(); + this.initChartProsesDinasTeknis(); + this.initChartPotensiTataRuang(); + } catch (e) { + console.error(e); + } + } + + async getBigDataResume(filterByDate) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterByDate}`, + { + 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; + } catch (error) { + console.error("Error fetching chart data:", error); + return null; + } + } + + initChartTargetPAD(filterDate) { + const year = + filterDate === "latest" + ? new Date().getFullYear() + : new Date(filterDate).getFullYear(); + document + .querySelectorAll(".document-title.chart-target-pad") + .forEach((element) => { + element.innerText = `Target PAD ${year}`; + }); + document + .querySelectorAll(".document-count.chart-target-pad") + .forEach((element) => { + element.innerText = ``; + }); + document + .querySelectorAll(".document-total.chart-target-pad") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + this.resumeBigData.target_pad.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-target-pad") + .forEach((element) => { + element.innerText = `${this.resumeBigData.target_pad.percentage}%`; + }); + } + initChartTotalPotensi() { + // const countAll = this.resultDataTotal.countData ?? 0; + + document + .querySelectorAll(".document-count.chart-total-potensi") + .forEach((element) => { + // element.innerText = `${countAll}`; + element.innerText = `${this.resumeBigData.total_potensi.count}`; + }); + document + .querySelectorAll(".document-total.chart-total-potensi") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.bigTotalPotensi.toString() + this.resumeBigData.total_potensi.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-total-potensi") + .forEach((element) => { + // element.innerText = `${this.resultPercentage}%`; + element.innerText = `${this.resumeBigData.total_potensi.percentage}%`; + }); + } + initChartVerificationDocuments() { + document + .querySelectorAll(".document-count.chart-berkas-terverifikasi") + .forEach((element) => { + // element.innerText = `${this.dataVerification.count}`; + element.innerText = `${this.resumeBigData.verified_document.count}`; + }); + document + .querySelectorAll(".document-total.chart-berkas-terverifikasi") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.bigTotalVerification.toString() + this.resumeBigData.verified_document.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-berkas-terverifikasi") + .forEach((element) => { + // element.innerText = `${this.percetageResultVerification}%`; + element.innerText = `${this.resumeBigData.verified_document.percentage}%`; + }); + } + initChartNonVerificationDocuments() { + document + .querySelectorAll( + ".document-count.chart-berkas-belum-terverifikasi" + ) + .forEach((element) => { + // element.innerText = `${this.dataNonVerification.count}`; + element.innerText = `${this.resumeBigData.non_verified_document.count}`; + }); + document + .querySelectorAll( + ".document-total.chart-berkas-belum-terverifikasi" + ) + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.bigTotalNonVerification.toString() + this.resumeBigData.non_verified_document.sum.toString() + )}`; + }); + document + .querySelectorAll( + ".small-percentage.chart-berkas-belum-terverifikasi" + ) + .forEach((element) => { + // element.innerText = `${this.percentageResultNonVerification}%`; + element.innerText = `${this.resumeBigData.non_verified_document.percentage}%`; + }); + } + initChartUsaha() { + document + .querySelectorAll(".document-count.chart-business") + .forEach((element) => { + // element.innerText = `${this.dataBusiness.count}`; + element.innerText = `${this.resumeBigData.business_document.count}`; + }); + document + .querySelectorAll(".document-total.chart-business") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.bigTotalBusiness.toString() + this.resumeBigData.business_document.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-business") + .forEach((element) => { + // element.innerText = `${this.percentageResultBusiness}%`; + element.innerText = `${this.resumeBigData.business_document.percentage}%`; + }); + } + initChartNonUsaha() { + document + .querySelectorAll(".document-count.chart-non-business") + .forEach((element) => { + // element.innerText = `${this.dataNonBusiness.count}`; + element.innerText = `${this.resumeBigData.non_business_document.count}`; + }); + document + .querySelectorAll(".document-total.chart-non-business") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.bigTotalNonBusiness.toString() + this.resumeBigData.non_business_document.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-non-business") + .forEach((element) => { + // element.innerText = `${this.percentageResultNonBusiness}%`; + element.innerText = `${this.resumeBigData.non_business_document.percentage}%`; + }); + } + initChartKekuranganPotensi() { + document + .querySelectorAll(".document-count.chart-kekurangan-potensi") + .forEach((element) => { + element.innerText = ``; + }); + document + .querySelectorAll(".document-total.chart-kekurangan-potensi") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.totalKekuranganPotensi.toString() + this.resumeBigData.kekurangan_potensi.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-kekurangan-potensi") + .forEach((element) => { + // element.innerText = `${this.percentageKekuranganPotensi}%`; + element.innerText = `${this.resumeBigData.kekurangan_potensi.percentage}%`; + }); + } + initChartRealisasiTerbitPBG() { + document + .querySelectorAll(".document-count.chart-realisasi-tebit-pbg") + .forEach((element) => { + // element.innerText = `${this.dataCountRealisasiTerbit}`; + element.innerText = `${this.resumeBigData.realisasi_terbit.count}`; + }); + document + .querySelectorAll(".document-total.chart-realisasi-tebit-pbg") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.dataSumRealisasiTerbit + this.resumeBigData.realisasi_terbit.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg") + .forEach((element) => { + element.innerText = `${this.resumeBigData.realisasi_terbit.percentage}%`; + }); + } + initChartMenungguKlikDPMPTSP() { + document + .querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp") + .forEach((element) => { + // element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`; + element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.count}`; + }); + document + .querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.dataSumMenungguKlikDPMPTSP + this.resumeBigData.menunggu_klik_dpmptsp.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp") + .forEach((element) => { + element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.percentage}%`; + }); + } + initChartProsesDinasTeknis() { + document + .querySelectorAll(".document-count.chart-proses-dinas-teknis") + .forEach((element) => { + // element.innerText = `${this.dataCountProsesDinasTeknis}`; + element.innerText = `${this.resumeBigData.proses_dinas_teknis.count}`; + }); + document + .querySelectorAll(".document-total.chart-proses-dinas-teknis") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + // this.dataSumProsesDinasTeknis + this.resumeBigData.proses_dinas_teknis.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-proses-dinas-teknis") + .forEach((element) => { + element.innerText = `${this.resumeBigData.proses_dinas_teknis.percentage}%`; + }); + } + initChartPotensiTataRuang() { + document + .querySelectorAll(".document-count.chart-potensi-tata-ruang") + .forEach((element) => { + element.innerText = `${this.resumeBigData.tata_ruang.count}`; + }); + document + .querySelectorAll(".document-total.chart-potensi-tata-ruang") + .forEach((element) => { + element.innerText = `Rp.${addThousandSeparators( + this.resumeBigData.tata_ruang.sum.toString() + )}`; + }); + document + .querySelectorAll(".small-percentage.chart-potensi-tata-ruang") + .forEach((element) => { + element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`; + }); + } +} + +document.addEventListener("DOMContentLoaded", async function (e) { + await new BigData().init(); +}); + +// function resizeDashboard() { +// //Target Width +// let targetElement = document.getElementById("dashboard-fixed-wrapper"); +// let targetWidth = targetElement.offsetWidth; +// //console.log("TARGET ",targetWidth); + +// //Real Object Width +// let dashboardElement = document.getElementById("dashboard-fixed-container"); +// let dashboardWidth = 1110; //dashboardElement.offsetWidth; +// //console.log("CURRENT ",dashboardWidth); + +// if (targetWidth > dashboardWidth) { +// targetWidth = dashboardWidth; +// } + +// dashboardElement.style.transformOrigin = "left top"; +// dashboardElement.style.transition = "transform 0.2s ease-in-out"; +// dashboardElement.style.transform = +// "scale(" + (targetWidth / dashboardWidth).toFixed(2) + ")"; +// //console.log("SCALE ", (targetWidth/dashboardWidth).toFixed(2)); +// } + +// window.addEventListener("load", function () { +// resizeDashboard(); +// }); + +// window.addEventListener("resize", function () { +// resizeDashboard(); +// }); + +function resizeDashboard() { + let targetElement = document.getElementById("dashboard-fixed-wrapper"); + let dashboardElement = document.getElementById("dashboard-fixed-container"); + + let targetWidth = targetElement.offsetWidth; + let dashboardWidth = 1110; + + let scaleFactor = (targetWidth / dashboardWidth).toFixed(2); + + // Prevent scaling beyond 1 (100%) to avoid overflow + scaleFactor = Math.min(scaleFactor, 1); + + dashboardElement.style.transformOrigin = "left top"; + dashboardElement.style.transition = "transform 0.2s ease-in-out"; + dashboardElement.style.transform = `scale(${scaleFactor})`; + + // Ensure horizontal scrolling is allowed if necessary + document.body.style.overflowX = "auto"; +} + +window.addEventListener("load", resizeDashboard); +window.addEventListener("resize", resizeDashboard); diff --git a/resources/js/settings/syncronize/syncronize.js b/resources/js/settings/syncronize/syncronize.js index 1782360..46db360 100644 --- a/resources/js/settings/syncronize/syncronize.js +++ b/resources/js/settings/syncronize/syncronize.js @@ -71,7 +71,6 @@ class SyncronizeTask { data.data.map((item) => [ item.id, item.message, - item.response_body, item.status, item.start_time, item.duration, diff --git a/resources/views/dashboards/leader.blade.php b/resources/views/dashboards/leader.blade.php new file mode 100644 index 0000000..918b1d5 --- /dev/null +++ b/resources/views/dashboards/leader.blade.php @@ -0,0 +1,187 @@ +@extends('layouts.vertical', ['subtitle' => 'Dashboards']) + +@section('css') +@vite(['resources/scss/dashboards/_bigdata.scss']) +@endsection + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Dashboards', 'subtitle' => 'Dashboard Pimpinan']) + +