converting src/ files to laravel php code
This commit is contained in:
parent
f45d1949e0
commit
e87558224e
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Handler extends ExceptionHandler
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $dontFlash = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the exception handling callbacks for the application.
|
||||||
|
*/
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$this->reportable(function (Throwable $e) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($request, Throwable $exception)
|
||||||
|
{
|
||||||
|
if ($exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) {
|
||||||
|
return response()->view('page404', [], 404);
|
||||||
|
}
|
||||||
|
return parent::render($request, $exception);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\ConfirmsPasswords;
|
||||||
|
|
||||||
|
class ConfirmPasswordController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Confirm Password Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller is responsible for handling password confirmations and
|
||||||
|
| uses a simple trait to include the behavior. You're free to explore
|
||||||
|
| this trait and override any functions that require customization.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use ConfirmsPasswords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users when the intended url fails.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
|
|
||||||
|
class ForgotPasswordController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Reset Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller is responsible for handling password reset emails and
|
||||||
|
| includes a trait which assists in sending these notifications from
|
||||||
|
| your application to your users. Feel free to explore this trait.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use SendsPasswordResetEmails;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
|
|
||||||
|
class LoginController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Login Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller handles authenticating users for the application and
|
||||||
|
| redirecting them to your home screen. The controller uses a trait
|
||||||
|
| to conveniently provide its functionality to your applications.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use AuthenticatesUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after login.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('guest')->except('logout');
|
||||||
|
$this->middleware('auth')->only('logout');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class RegisterController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Register Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller handles the registration of new users as well as their
|
||||||
|
| validation and creation. By default this controller uses a trait to
|
||||||
|
| provide this functionality without requiring any additional code.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use RegistersUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after registration.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('guest');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a validator for an incoming registration request.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return \Illuminate\Contracts\Validation\Validator
|
||||||
|
*/
|
||||||
|
protected function validator(array $data)
|
||||||
|
{
|
||||||
|
return Validator::make($data, [
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||||
|
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new user instance after a valid registration.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return \App\Models\User
|
||||||
|
*/
|
||||||
|
protected function create(array $data)
|
||||||
|
{
|
||||||
|
return User::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'password' => Hash::make($data['password']),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
|
|
||||||
|
class ResetPasswordController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Password Reset Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller is responsible for handling password reset requests
|
||||||
|
| and uses a simple trait to include this behavior. You're free to
|
||||||
|
| explore this trait and override any methods you wish to tweak.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use ResetsPasswords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after resetting their password.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = '/home';
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Foundation\Auth\VerifiesEmails;
|
||||||
|
|
||||||
|
class VerificationController extends Controller
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Email Verification Controller
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This controller is responsible for handling email verification for any
|
||||||
|
| user that recently registered with the application. Emails may also
|
||||||
|
| be re-sent if the user didn't receive the original email message.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use VerifiesEmails;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where to redirect users after verification.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $redirectTo = '/home';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
$this->middleware('signed')->only('verify');
|
||||||
|
$this->middleware('throttle:6,1')->only('verify', 'resend');
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
abstract class Controller
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
{
|
{
|
||||||
//
|
use AuthorizesRequests, ValidatesRequests;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
|
class CustomTableController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$data = User::query();
|
||||||
|
$search = $request->input('search');
|
||||||
|
if ($search) {
|
||||||
|
$data->where('name', 'like', "%$search%");
|
||||||
|
}
|
||||||
|
$sortField = $request->input('sort_field');
|
||||||
|
$sortOrder = $request->input('sort_order', 'asc');
|
||||||
|
if ($sortField) {
|
||||||
|
$data->orderBy($sortField, $sortOrder);
|
||||||
|
}
|
||||||
|
$perPage = $request->input('per_page', 10);
|
||||||
|
$paginatedData = $data->paginate($perPage);
|
||||||
|
$columns = [
|
||||||
|
['title' => 'ID', 'dataIndex' => 'id'],
|
||||||
|
['title' => 'Name', 'dataIndex' => 'name'],
|
||||||
|
];
|
||||||
|
$actions = [
|
||||||
|
['type' => 'edit', 'name' => 'Edit', 'path' => '/edit', 'access' => true],
|
||||||
|
['type' => 'delete', 'name' => 'Delete', 'path' => '#', 'access' => true],
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('custom-table', compact('paginatedData', 'columns', 'actions'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
User::find($id)->delete();
|
||||||
|
return redirect()->back()->with('success', 'User deleted');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class BreadcrumbController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$path = $request->path();
|
||||||
|
$pathSnippets = array_filter(explode('/', $path));
|
||||||
|
$root = empty($pathSnippets);
|
||||||
|
|
||||||
|
// Simulated pageRoutes (replace with your actual routes)
|
||||||
|
$pageRoutes = [
|
||||||
|
['path' => '/my-profile', 'name' => 'Home'],
|
||||||
|
['path' => '/my-profile/users', 'name' => 'Users', 'params' => true],
|
||||||
|
];
|
||||||
|
|
||||||
|
$extraBreadcrumbItems = [];
|
||||||
|
foreach ($pathSnippets as $index => $snippet) {
|
||||||
|
$url = '/' . implode('/', array_slice($pathSnippets, 0, $index + 1));
|
||||||
|
$route = collect($pageRoutes)->firstWhere('path', $url);
|
||||||
|
if ($route) {
|
||||||
|
$paramsId = end($pathSnippets);
|
||||||
|
$extraBreadcrumbItems[] = [
|
||||||
|
'url' => $route['params'] ? "$url/$paramsId" : $url,
|
||||||
|
'name' => $route['name'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('dashboard.breadcrumbs', compact('root', 'extraBreadcrumbItems'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ContentController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
// Simulated pageRoutes (replace with your actual routes)
|
||||||
|
$pageRoutes = [
|
||||||
|
['path' => '/my-profile', 'name' => 'Home'],
|
||||||
|
['path' => '/my-profile/users', 'name' => 'Users', 'params' => true],
|
||||||
|
];
|
||||||
|
$root = $request->path() === '/my-profile';
|
||||||
|
|
||||||
|
return view('dashboard.content', compact('pageRoutes', 'root'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class DashboardController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (!$user) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
|
||||||
|
$collapsed = session()->get('collapsed', false);
|
||||||
|
$pageRoutes = [
|
||||||
|
['path' => '/my-profile', 'name' => 'Home'],
|
||||||
|
['path' => '/my-profile/users', 'name' => 'Users', 'params' => true],
|
||||||
|
];
|
||||||
|
$root = $request->path() === '/my-profile';
|
||||||
|
|
||||||
|
return view('dashboard.layout', compact('collapsed', 'user', 'pageRoutes', 'root'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(Request $request)
|
||||||
|
{
|
||||||
|
Auth::logout();
|
||||||
|
return redirect()->route('login')->with('success', 'Logged out due to inactivity');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class DataDisplayController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (!$user) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = User::all();
|
||||||
|
$layout = 'horizontal';
|
||||||
|
$avatar = true;
|
||||||
|
$viewPath = '/view';
|
||||||
|
$header = 'User List';
|
||||||
|
$footer = 'Total: ' . count($data) . ' users';
|
||||||
|
|
||||||
|
return view('dashboard.data-display', compact('data', 'layout', 'avatar', 'viewPath', 'header', 'footer'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class FooterController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
return view('dashboard.footer');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class HeaderController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (!$user) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
$collapsed = session()->get('collapsed', false); // Simulate toggle state
|
||||||
|
return view('dashboard.header', compact('user', 'collapsed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggle(Request $request)
|
||||||
|
{
|
||||||
|
$collapsed = !$request->input('collapsed', false);
|
||||||
|
session()->put('collapsed', $collapsed);
|
||||||
|
return response()->json(['collapsed' => $collapsed]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(Request $request)
|
||||||
|
{
|
||||||
|
Auth::logout();
|
||||||
|
return redirect()->route('login')->with('success', 'Logged out successfully');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class RouteController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(Request $request, $component = null)
|
||||||
|
{
|
||||||
|
$view = $component ? "dashboard.{$component}" : 'dashboard.default';
|
||||||
|
if (!view()->exists($view)) {
|
||||||
|
return redirect()->route('login')->with('error', 'You must log in to enter this page');
|
||||||
|
}
|
||||||
|
return view('dashboard.route', compact('view'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class SidebarController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
if (!$user) {
|
||||||
|
return redirect()->route('login');
|
||||||
|
}
|
||||||
|
|
||||||
|
$collapsed = session()->get('collapsed', false);
|
||||||
|
$navigation = [
|
||||||
|
['key' => 0, 'label' => 'User Management', 'path' => '/user-management', 'icon' => 'team', 'access' => $user && $user->role == 1],
|
||||||
|
['key' => 9, 'label' => 'Notifications', 'path' => '/notifications', 'icon' => 'notification', 'access' => true],
|
||||||
|
['key' => 4, 'label' => 'Member Management', 'path' => '/member-management', 'icon' => 'credit-card', 'access' => $user && $user->role == 1, 'child' => [
|
||||||
|
['key' => 0.0, 'label' => 'Card Member', 'path' => '/member-management/card-member', 'access' => true],
|
||||||
|
['key' => 0.1, 'label' => 'Locked Accounts', 'path' => '/member-management/lock-account', 'access' => true],
|
||||||
|
]],
|
||||||
|
['key' => 8, 'label' => 'Home Page ( Mobile )', 'path' => '/home-page', 'icon' => 'home', 'access' => true, 'child' => [
|
||||||
|
['key' => 0.0, 'label' => 'Photo Slider', 'path' => '/home-page/photo-slider', 'access' => true],
|
||||||
|
]],
|
||||||
|
['key' => 3, 'label' => 'Promotions', 'path' => '/promotions', 'icon' => 'tags', 'access' => true],
|
||||||
|
['key' => 2, 'label' => 'Top-Up', 'path' => '/top-up', 'icon' => 'plus-circle', 'access' => $user && $user->role == 1],
|
||||||
|
['key' => 6, 'label' => 'About Us', 'path' => '/about-us', 'icon' => 'info-circle', 'access' => $user && $user->role == 1, 'child' => [
|
||||||
|
['key' => 0.6, 'label' => 'Card Types', 'path' => '/about-us/card-types', 'access' => true],
|
||||||
|
['key' => 0.5, 'label' => 'Terms & Privacy', 'path' => '/about-us/term-privacy', 'access' => true],
|
||||||
|
]],
|
||||||
|
['key' => 7, 'label' => 'Reports', 'path' => '/reports', 'icon' => 'file-text', 'access' => true, 'child' => [
|
||||||
|
['key' => 0.7, 'label' => 'Registration Report', 'path' => '/reports/registration-report', 'access' => true],
|
||||||
|
['key' => 0.8, 'label' => 'Top-Up Usage Report', 'path' => '/reports/top-up', 'access' => true],
|
||||||
|
['key' => 0.9, 'label' => 'Mobile Usage Report', 'path' => '/reports/mobile-report', 'access' => true],
|
||||||
|
['key' => 0.1, 'label' => 'Station Rating Report', 'path' => '/reports/station-rating', 'access' => true],
|
||||||
|
]],
|
||||||
|
['key' => 8, 'label' => 'System Parameters', 'path' => '/system-parameters', 'icon' => 'setting', 'access' => $user && $user->role == 1],
|
||||||
|
['key' => 12, 'label' => 'Station Locator', 'path' => '', 'icon' => 'environment', 'access' => true, 'child' => [
|
||||||
|
['key' => 0.11, 'label' => 'Branches', 'path' => '/branches', 'access' => true],
|
||||||
|
['key' => 0.12, 'label' => 'Stations', 'path' => '/stations', 'access' => true],
|
||||||
|
['key' => 0.13, 'label' => 'Fuels', 'path' => '/fuels', 'access' => true],
|
||||||
|
]],
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('dashboard.sidebar', compact('collapsed', 'navigation', 'user'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Dropdown;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Illuminate\Support\Facades\Response;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Export;
|
||||||
|
|
||||||
|
class DropdownExportController extends Controller
|
||||||
|
{
|
||||||
|
public function export(Request $request)
|
||||||
|
{
|
||||||
|
$params = $request->all();
|
||||||
|
if ($request->has('defaultFilter')) {
|
||||||
|
$params = array_merge($params, $request->input('defaultFilter'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = User::all()->map(function ($user) {
|
||||||
|
return implode(',', [$user->id, $user->name]) . "\n";
|
||||||
|
})->implode('');
|
||||||
|
|
||||||
|
$filename = 'export_' . now()->format('mmddyyyy') . '.csv';
|
||||||
|
Storage::put("exports/{$filename}", $data);
|
||||||
|
|
||||||
|
Export::create(['filename' => $filename, 'exported_at' => now()]);
|
||||||
|
|
||||||
|
return Response::download(storage_path("app/exports/{$filename}"))->deleteFileAfterSend(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class HomeController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new controller instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->middleware('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the application dashboard.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Contracts\Support\Renderable
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('home');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class LoadingController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
return view('loading');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Login;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class LayoutController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
return view('login.layout');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Login;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class RouteController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request, $component = null)
|
||||||
|
{
|
||||||
|
$view = $component ? "login.{$component}" : 'login.default';
|
||||||
|
if (!view()->exists($view)) {
|
||||||
|
$view = 'login.default';
|
||||||
|
}
|
||||||
|
return view('login.route', compact('view'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Modal;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ModalCancelController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$path = $request->input('path', '/');
|
||||||
|
$title = $request->input('title', 'Your Title');
|
||||||
|
$message = $request->input('message', 'Your Message');
|
||||||
|
$dirty = $request->input('dirty', true);
|
||||||
|
$name = $request->input('name', 'Cancel');
|
||||||
|
|
||||||
|
return view('modal.cancel', compact('path', 'title', 'message', 'dirty', 'name'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm(Request $request)
|
||||||
|
{
|
||||||
|
return redirect($request->input('path', '/'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Notification;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
|
class NotificationTablesController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$data = Notification::query();
|
||||||
|
$search = $request->input('search');
|
||||||
|
if ($search) {
|
||||||
|
$data->where('title', 'like', "%$search%");
|
||||||
|
}
|
||||||
|
$sortField = $request->input('_sort_by');
|
||||||
|
$sortOrder = $request->input('_sort_order', 'asc') === 'asc' ? 'asc' : 'desc';
|
||||||
|
if ($sortField) {
|
||||||
|
$data->orderBy($sortField, $sortOrder);
|
||||||
|
}
|
||||||
|
$dateStart = $request->input('date_start');
|
||||||
|
$dateEnd = $request->input('date_end');
|
||||||
|
if ($dateStart && $dateEnd) {
|
||||||
|
$data->whereBetween('created_at', [$dateStart, $dateEnd]);
|
||||||
|
}
|
||||||
|
$perPage = $request->input('page_size', 10);
|
||||||
|
$currentPage = $request->input('page', 1);
|
||||||
|
$paginatedData = $data->paginate($perPage);
|
||||||
|
|
||||||
|
$columns = [
|
||||||
|
['title' => 'ID', 'dataIndex' => 'id'],
|
||||||
|
['title' => 'Title', 'dataIndex' => 'title'],
|
||||||
|
];
|
||||||
|
$actions = [
|
||||||
|
['key' => 'edit', 'title' => 'Edit', 'icon' => 'edit'],
|
||||||
|
['key' => 'delete', 'title' => 'Delete', 'icon' => 'delete'],
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('notification-tables', compact('paginatedData', 'columns', 'actions'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
Notification::find($id)->delete();
|
||||||
|
return redirect()->back()->with('success', 'Notification deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchDestroy(Request $request)
|
||||||
|
{
|
||||||
|
$ids = $request->input('ids', []);
|
||||||
|
Notification::whereIn('id', $ids)->delete();
|
||||||
|
return redirect()->back()->with('success', 'Notifications deleted');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Page404Controller extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
return view('page404');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
|
class UserController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$data = User::query();
|
||||||
|
$search = $request->input('search');
|
||||||
|
if ($search) {
|
||||||
|
$data->where('name', 'like', "%$search%");
|
||||||
|
}
|
||||||
|
$sortField = $request->input('sort_field');
|
||||||
|
$sortOrder = $request->input('sort_order', 'asc');
|
||||||
|
if ($sortField) {
|
||||||
|
$data->orderBy($sortField, $sortOrder);
|
||||||
|
}
|
||||||
|
$paginatedData = $data->paginate($request->input('per_page', 10));
|
||||||
|
return view('users', compact('paginatedData'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Export extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['filename', 'exported_at'];
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Notification extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['title', 'content', 'created_at'];
|
||||||
|
}
|
|
@ -17,11 +17,8 @@ class User extends Authenticatable
|
||||||
*
|
*
|
||||||
* @var list<string>
|
* @var list<string>
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = ['name', 'first_name', 'last_name', 'avatar'];
|
||||||
'name',
|
|
||||||
'email',
|
|
||||||
'password',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that should be hidden for serialization.
|
* The attributes that should be hidden for serialization.
|
||||||
|
@ -45,4 +42,5 @@ class User extends Authenticatable
|
||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"laravel/framework": "^11.31",
|
"laravel/framework": "^11.31",
|
||||||
"laravel/tinker": "^2.9"
|
"laravel/tinker": "^2.9",
|
||||||
|
"laravel/ui": "^4.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "626b9e7ddd47fb7eff9aaa53cce0c9ad",
|
"content-hash": "a7159c5237c3c2e75dbd1d23c85e732c",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
@ -1455,6 +1455,69 @@
|
||||||
},
|
},
|
||||||
"time": "2025-01-27T14:24:01+00:00"
|
"time": "2025-01-27T14:24:01+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/ui",
|
||||||
|
"version": "v4.6.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/ui.git",
|
||||||
|
"reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/ui/zipball/7d6ffa38d79f19c9b3e70a751a9af845e8f41d88",
|
||||||
|
"reference": "7d6ffa38d79f19c9b3e70a751a9af845e8f41d88",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/console": "^9.21|^10.0|^11.0|^12.0",
|
||||||
|
"illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0",
|
||||||
|
"illuminate/support": "^9.21|^10.0|^11.0|^12.0",
|
||||||
|
"illuminate/validation": "^9.21|^10.0|^11.0|^12.0",
|
||||||
|
"php": "^8.0",
|
||||||
|
"symfony/console": "^6.0|^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^7.35|^8.15|^9.0|^10.0",
|
||||||
|
"phpunit/phpunit": "^9.3|^10.4|^11.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Laravel\\Ui\\UiServiceProvider"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "4.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laravel\\Ui\\": "src/",
|
||||||
|
"Illuminate\\Foundation\\Auth\\": "auth-backend/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Taylor Otwell",
|
||||||
|
"email": "taylor@laravel.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel UI utilities and presets.",
|
||||||
|
"keywords": [
|
||||||
|
"laravel",
|
||||||
|
"ui"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/laravel/ui/tree/v4.6.1"
|
||||||
|
},
|
||||||
|
"time": "2025-01-28T15:15:29+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "league/commonmark",
|
"name": "league/commonmark",
|
||||||
"version": "2.6.1",
|
"version": "2.6.1",
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateUsersTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('users', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('first_name')->nullable();
|
||||||
|
$table->string('last_name')->nullable();
|
||||||
|
$table->string('avatar')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('users');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateExportsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('exports', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('filename');
|
||||||
|
$table->timestamp('exported_at');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('exports');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateNotificationsTable extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('notifications', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('title');
|
||||||
|
$table->text('content')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('notifications');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +1,40 @@
|
||||||
version: '3.8' # Upgrade version for healthcheck support
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# Backend: Laravel
|
|
||||||
laravel-app:
|
laravel-app:
|
||||||
image: laravel-app:latest
|
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/laravel/Dockerfile
|
dockerfile: docker/laravel/Dockerfile
|
||||||
|
image: laravel-app:latest
|
||||||
volumes:
|
volumes:
|
||||||
- ./app:/var/www/html
|
- ./app:/var/www/html
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl -f http://localhost || exit 1"]
|
test: ["CMD", "pgrep", "php-fpm"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 30s
|
start_period: 30s
|
||||||
|
|
||||||
# Frontend: React (served with Nginx inside container)
|
nginx:
|
||||||
react-app:
|
image: nginx:latest
|
||||||
build:
|
|
||||||
context: ./frontend
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./frontend:/app
|
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
- ./public:/var/www/html/public
|
||||||
|
- ./frontend/build:/usr/share/nginx/html
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
networks:
|
networks:
|
||||||
- app-network
|
- app-network
|
||||||
command: "nginx -g 'daemon off;'"
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 20s
|
|
||||||
|
|
||||||
# Nginx: Serves both Laravel and React
|
|
||||||
nginx:
|
|
||||||
image: nginx:latest
|
|
||||||
volumes:
|
|
||||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
- ./public:/var/www/html/public
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
networks:
|
|
||||||
- app-network
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- laravel-app
|
laravel-app:
|
||||||
|
condition: service_healthy
|
||||||
|
react-app:
|
||||||
|
condition: service_healthy
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "curl", "-f", "http://localhost"]
|
test: ["CMD", "curl", "-f", "http://localhost"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
|
@ -58,7 +42,6 @@ services:
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 20s
|
start_period: 20s
|
||||||
|
|
||||||
# MySQL: Database
|
|
||||||
mysql:
|
mysql:
|
||||||
image: mysql:5.7
|
image: mysql:5.7
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
# Set up PHP for Laravel
|
# Set up PHP for Laravel
|
||||||
FROM php:8.1-fpm
|
FROM php:8.2-fpm
|
||||||
|
|
||||||
# Install necessary extensions and dependencies for Laravel
|
# Install necessary system dependencies, including oniguruma
|
||||||
RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev libfreetype6-dev zip git unzip
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libpng-dev \
|
||||||
|
libjpeg-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
zip \
|
||||||
|
git \
|
||||||
|
unzip \
|
||||||
|
libonig-dev && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install PHP extensions
|
# Install PHP extensions
|
||||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
|
RUN docker-php-ext-configure gd --with-freetype --with-jpeg && \
|
||||||
docker-php-ext-install gd
|
docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
|
||||||
|
|
||||||
# Install Composer
|
# Install Composer
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
@ -14,12 +22,19 @@ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
# Copy Laravel files into container
|
# Copy entire Laravel project (including artisan)
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Install PHP dependencies
|
# Install PHP dependencies
|
||||||
RUN composer install --no-interaction --optimize-autoloader --no-dev
|
RUN composer install --no-interaction --optimize-autoloader --no-dev
|
||||||
|
|
||||||
|
# Set permissions for storage and cache
|
||||||
|
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
|
||||||
|
RUN chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
|
||||||
|
|
||||||
|
# Make artisan executable
|
||||||
|
RUN chmod +x /var/www/html/artisan
|
||||||
|
|
||||||
# Expose port for PHP-FPM
|
# Expose port for PHP-FPM
|
||||||
EXPOSE 9000
|
EXPOSE 9000
|
||||||
|
|
||||||
|
|
|
@ -4,33 +4,35 @@ server {
|
||||||
|
|
||||||
# Serve the React app (static files)
|
# Serve the React app (static files)
|
||||||
location / {
|
location / {
|
||||||
root /var/www/html; # React build output directory
|
root /usr/share/nginx/html; # Point to React build output
|
||||||
try_files $uri $uri/ /index.html; # Ensure React routing works properly with single-page app
|
try_files $uri $uri/ /index.html; # Ensure React routing works
|
||||||
|
index index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Proxy API requests to Laravel (assuming Laravel is running on port 9000)
|
# Proxy API requests to Laravel
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://laravel-app:9000; # Laravel service name from Docker Compose
|
proxy_pass http://laravel-app:9000;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Optionally, handle assets like images or other media files
|
# Handle PHP files for Laravel
|
||||||
|
location ~ ^/index.php(/|$) {
|
||||||
|
fastcgi_pass laravel-app:9000;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Serve static files (e.g., from Laravel public)
|
||||||
|
location /static/ {
|
||||||
|
root /var/www/html/public; # Laravel static files
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache static assets
|
||||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|xml)$ {
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|pdf|txt|xml)$ {
|
||||||
expires 365d;
|
expires 365d;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle other static files
|
|
||||||
location /static/ {
|
|
||||||
root /var/www/html; # React static files
|
|
||||||
}
|
|
||||||
|
|
||||||
# Prevent caching for the Laravel dynamic content
|
|
||||||
location ~ \.php$ {
|
|
||||||
fastcgi_pass laravel-app:9000; # Forward PHP requests to the Laravel app
|
|
||||||
fastcgi_param SCRIPT_FILENAME /var/www/html/public$fastcgi_script_name;
|
|
||||||
include fastcgi_params;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -21,7 +21,7 @@ RUN yarn install # Install dependencies
|
||||||
COPY . /usr/src/app
|
COPY . /usr/src/app
|
||||||
|
|
||||||
# Build the app
|
# Build the app
|
||||||
RUN yarn build
|
RUN yarn build --ignore-scripts
|
||||||
|
|
||||||
# Stage 2 - the production environment
|
# Stage 2 - the production environment
|
||||||
FROM nginx:1.13.9-alpine
|
FROM nginx:1.13.9-alpine
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"react-redux": "^5.0.7",
|
"react-redux": "^5.0.7",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-router-redux": "^4.0.8",
|
"react-router-redux": "^4.0.8",
|
||||||
"react-scripts": "1.1.5",
|
"react-scripts": "^5.0.1",
|
||||||
"redux": "^4.0.0",
|
"redux": "^4.0.0",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-saga": "^0.16.0",
|
"redux-saga": "^0.16.0",
|
||||||
|
@ -45,14 +45,15 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-app-rewired start",
|
"start": "react-app-rewired start",
|
||||||
"build": "react-scripts build && cp -r build/* ../public",
|
|
||||||
"test": "react-app-rewired test --env=jsdom",
|
"test": "react-app-rewired test --env=jsdom",
|
||||||
"eject": "react-app-rewired eject",
|
"eject": "react-app-rewired eject",
|
||||||
"dev": "docker-compose up -d --build",
|
"dev": "docker-compose up -d --build",
|
||||||
"dev:stop": "docker-compose down",
|
"dev:stop": "docker-compose down",
|
||||||
"prod": "docker-compose -f docker-compose-prod.yml up -d --build",
|
"prod": "docker-compose -f docker-compose-prod.yml up -d --build",
|
||||||
"prod:stop": "docker-compose -f docker-compose-prod.yml down",
|
"prod:stop": "docker-compose -f docker-compose-prod.yml down",
|
||||||
"flow": "flow"
|
"flow": "flow",
|
||||||
|
"build": "react-scripts build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-import": "^1.7.0",
|
"babel-plugin-import": "^1.7.0",
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
export function customAction({type, payload}) {
|
|
||||||
return {
|
|
||||||
type: type,
|
|
||||||
payload
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,683 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
Input,
|
|
||||||
Icon,
|
|
||||||
Divider,
|
|
||||||
Tooltip,
|
|
||||||
Popconfirm,
|
|
||||||
message,
|
|
||||||
Button,
|
|
||||||
Pagination,
|
|
||||||
Row,
|
|
||||||
Col
|
|
||||||
} from "antd";
|
|
||||||
//import { callApi } from "utils/Api";
|
|
||||||
import { Link, withRouter } from "react-router-dom";
|
|
||||||
import querystring from "querystring";
|
|
||||||
import { encrypt, decrypt } from 'utils/encrypto'
|
|
||||||
// const querystring = require("querystring");
|
|
||||||
const { Column, ColumnGroup } = Table;
|
|
||||||
const Search = Input.Search;
|
|
||||||
|
|
||||||
class CustomTable extends React.PureComponent {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
data: [],
|
|
||||||
loading: false,
|
|
||||||
filteredInfo: null,
|
|
||||||
sortedInfo: null,
|
|
||||||
pageName: '_page',
|
|
||||||
currentPage: 1,
|
|
||||||
sizeName: '_limit',
|
|
||||||
pageSize: 10,
|
|
||||||
totalData: 100,
|
|
||||||
searchValue: null,
|
|
||||||
headerColumns: props.columns.map(column => {
|
|
||||||
if (column.sorter) {
|
|
||||||
column.sorter = (a, b) => a[column.dataIndex] - b[column.dataIndex];
|
|
||||||
column.sortOrder = null;
|
|
||||||
}
|
|
||||||
return column;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.handleInitialLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
|
||||||
const { currentPage, pageSize, pageName } = this.state;
|
|
||||||
|
|
||||||
if (prevProps.location.search !== this.props.location.search) {
|
|
||||||
|
|
||||||
if (this.props.location.search === "") {
|
|
||||||
this.handleclearAll();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevState.sortedInfo !== this.state.sortedInfo) {
|
|
||||||
//Sort Columns Function
|
|
||||||
this.setState({
|
|
||||||
headerColumns: this.state.headerColumns.map(column => {
|
|
||||||
if (
|
|
||||||
this.state.sortedInfo &&
|
|
||||||
this.state.sortedInfo.columnKey === column.dataIndex
|
|
||||||
) {
|
|
||||||
column.sortOrder = this.state.sortedInfo.order;
|
|
||||||
}
|
|
||||||
return column;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
handleInitialLoad = () => {
|
|
||||||
const { search } = this.props.location;
|
|
||||||
const { currentPage, pageSize, pageName, sizeName } = this.state;
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
let parsed = querystring.parse(search.substring(1));
|
|
||||||
console.log('====================================');
|
|
||||||
console.log(parsed,pageName, sizeName,parsed[pageName], parsed[sizeName], "&&&&&&SEARCH!!");
|
|
||||||
console.log('====================================');
|
|
||||||
|
|
||||||
if (parsed) {
|
|
||||||
if (parsed[pageName] && parsed[sizeName] && parsed.q && parsed._sort && parsed._order ) {
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: parseInt(parsed[pageName]),
|
|
||||||
size: parseInt(parsed[sizeName]),
|
|
||||||
search: parsed.q
|
|
||||||
});
|
|
||||||
} else if (parsed[pageName] && parsed[sizeName] && parsed.q) {
|
|
||||||
alert("Search")
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: parseInt(parsed[pageName]),
|
|
||||||
size: parseInt(parsed[sizeName]),
|
|
||||||
search: parsed.q
|
|
||||||
});
|
|
||||||
} else if (parsed[pageName] && parsed[sizeName]) {
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: parseInt(parsed[pageName]),
|
|
||||||
size: parseInt(parsed[sizeName]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// this.fetch({
|
|
||||||
// ...parsed
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: currentPage,
|
|
||||||
size: pageSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTableChange = (pagination, filters, sorter) => {
|
|
||||||
const { currentPage, pageSize, searchValue } = this.state;
|
|
||||||
console.log("====================================");
|
|
||||||
console.log(filters, sorter, "tesadasdas");
|
|
||||||
console.log("====================================");
|
|
||||||
// this.setState({
|
|
||||||
// filteredInfo: filters,
|
|
||||||
// sortedInfo: sorter
|
|
||||||
// });
|
|
||||||
if (Object.keys(sorter).length !== 0) {
|
|
||||||
// this.fetch({
|
|
||||||
// _page: currentPage,
|
|
||||||
// _limit: pageSize,
|
|
||||||
// _sort: sorter.field,
|
|
||||||
// _order: sorter.order === "ascend" ? "asc" : "desc",
|
|
||||||
|
|
||||||
// ...filters
|
|
||||||
// });
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
this.handleUpdateData({
|
|
||||||
sort: sorter,
|
|
||||||
filter: filters,
|
|
||||||
search: searchValue
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({ sort: sorter, filter: filters });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (searchValue) {
|
|
||||||
this.handleUpdateData({ filter: filters, search: searchValue });
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({ filter: filters });
|
|
||||||
}
|
|
||||||
// this.fetch({
|
|
||||||
// _page: currentPage,
|
|
||||||
// _limit: pageSize,
|
|
||||||
// ...filters
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch = async (params = {}) => {
|
|
||||||
let { url, history } = this.props;
|
|
||||||
const stringified = querystring.stringify(params);
|
|
||||||
|
|
||||||
console.log("GGGGG3333", url.default, stringified, params, window.location);
|
|
||||||
this.setState({ loading: true });
|
|
||||||
// history.push(`${url.default}?${stringified}`);
|
|
||||||
history.push({
|
|
||||||
pathname: url.default,
|
|
||||||
search: stringified
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
// let response = await callApi({
|
|
||||||
// url: url.default,
|
|
||||||
// params: {
|
|
||||||
// _page: params._page,
|
|
||||||
// _limit: params._limit,
|
|
||||||
// ...params
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (response.status === 200) {
|
|
||||||
// this.setState({
|
|
||||||
// loading: false,
|
|
||||||
// data: response.data,
|
|
||||||
// totalData: response.data.total ? response.data.total : 100
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
} catch (error) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpdateData = ({ search, sort, page, size, filter }) => {
|
|
||||||
const {
|
|
||||||
currentPage,
|
|
||||||
pageSize,
|
|
||||||
filteredInfo,
|
|
||||||
sortedInfo,
|
|
||||||
searchValue
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
console.log("====================================");
|
|
||||||
console.log(
|
|
||||||
search, sort, page, size, filter,
|
|
||||||
"WWOOOPS!!"
|
|
||||||
);
|
|
||||||
console.log("====================================");
|
|
||||||
|
|
||||||
if (search && sort && filter) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
sortedInfo: sort,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc",
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
|
|
||||||
//filteredInfo value
|
|
||||||
} else if (filter) {
|
|
||||||
if (sort) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
sortedInfo: sort,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc",
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
} else if (search) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search,
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//sortedInfo value
|
|
||||||
} else if (sort) {
|
|
||||||
if (filter) {
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
sortedInfo: sort,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc",
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
} else if (search) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sortedInfo: sort,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
sortedInfo: sort,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//search Value
|
|
||||||
} else if (search) {
|
|
||||||
if (filter) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: filter,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search,
|
|
||||||
...filter
|
|
||||||
});
|
|
||||||
} else if (sort) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
sortedInfo: sort,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search,
|
|
||||||
_sort: sort.field,
|
|
||||||
_order: sort.order === "ascend" ? "asc" : "desc"
|
|
||||||
});
|
|
||||||
} else if (page && size) {
|
|
||||||
alert(page, size, "OOOPS@")
|
|
||||||
this.setState({
|
|
||||||
currentPage: page,
|
|
||||||
pageSize: size,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
this.fetch({
|
|
||||||
_page: page,
|
|
||||||
_limit: size,
|
|
||||||
q: search
|
|
||||||
});
|
|
||||||
} else if (page) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
currentPage: page,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: page,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search
|
|
||||||
});
|
|
||||||
} else if (size) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
pageSize: size,
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: size,
|
|
||||||
q: search
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
searchValue: search,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize,
|
|
||||||
q: search
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (page && size) {
|
|
||||||
this.setState({
|
|
||||||
currentPage: page,
|
|
||||||
pageSize: size,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: page,
|
|
||||||
_limit: size
|
|
||||||
});
|
|
||||||
} else if (page) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
currentPage: page,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: page,
|
|
||||||
_limit: pageSize
|
|
||||||
});
|
|
||||||
} else if (size) {
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
pageSize: size,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: size
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.fetch({
|
|
||||||
_page: currentPage,
|
|
||||||
_limit: pageSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSearch = e => {
|
|
||||||
const { currentPage, pageSize, filteredInfo, sortedInfo } = this.state;
|
|
||||||
console.log("====================================");
|
|
||||||
console.log(e);
|
|
||||||
console.log("====================================");
|
|
||||||
// this.setState({
|
|
||||||
// searchValue: e
|
|
||||||
// });
|
|
||||||
if (e) {
|
|
||||||
this.handleUpdateData({ search: e, page: 1 });
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({ search: null, page: 1 });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSearchChange = e => {
|
|
||||||
const { currentPage, pageSize, filteredInfo, sortedInfo } = this.state;
|
|
||||||
|
|
||||||
// this.setState({
|
|
||||||
// searchValue: e
|
|
||||||
// });
|
|
||||||
if (e) {
|
|
||||||
this.setState({
|
|
||||||
searchValue: e.target.value
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
searchValue: null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDeleteConfirmYes = e => {
|
|
||||||
console.log(e);
|
|
||||||
message.success("Click on Yes");
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDeleteConfirmNo = e => {
|
|
||||||
console.log(e);
|
|
||||||
message.error("Click on No");
|
|
||||||
};
|
|
||||||
handleclearAll = () => {
|
|
||||||
console.log("====================================");
|
|
||||||
console.log("reset");
|
|
||||||
console.log("====================================");
|
|
||||||
this.setState({
|
|
||||||
filteredInfo: null,
|
|
||||||
sortedInfo: null,
|
|
||||||
searchValue: null,
|
|
||||||
// currentPage: 1,
|
|
||||||
// pageSize: 10
|
|
||||||
});
|
|
||||||
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: 1,
|
|
||||||
size: 10,
|
|
||||||
});
|
|
||||||
// this.fetch({
|
|
||||||
// _page: 1,
|
|
||||||
// _limit: 10
|
|
||||||
// });
|
|
||||||
};
|
|
||||||
handlePagination = page => {
|
|
||||||
const { pageSize, searchValue } = this.state;
|
|
||||||
|
|
||||||
// this.fetch({
|
|
||||||
// _page: page,
|
|
||||||
// _limit: pageSize
|
|
||||||
// });
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
this.handleUpdateData({ page, search: searchValue });
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({ page });
|
|
||||||
}
|
|
||||||
|
|
||||||
// this.setState({
|
|
||||||
// currentPage: page
|
|
||||||
// });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSizeChange = (current, pageSize) => {
|
|
||||||
console.log("TEST!", current, pageSize, searchValue);
|
|
||||||
const { searchValue } = this.state;
|
|
||||||
// this.fetch({
|
|
||||||
// _page: current,
|
|
||||||
// _limit: pageSize
|
|
||||||
// });
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
this.handleUpdateData({
|
|
||||||
page: current,
|
|
||||||
size: pageSize,
|
|
||||||
search: searchValue
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.handleUpdateData({ page: current, size: pageSize });
|
|
||||||
}
|
|
||||||
|
|
||||||
// this.setState({
|
|
||||||
// currentPage: current,
|
|
||||||
// pageSize
|
|
||||||
// });
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
handleRenderActionButton = ({action, item}) => {
|
|
||||||
let {keyValue} = this.props
|
|
||||||
|
|
||||||
let idValue = item[keyValue].toString()
|
|
||||||
console.log('====================================');
|
|
||||||
|
|
||||||
console.log('====================================');
|
|
||||||
switch (action.type) {
|
|
||||||
case 'edit':
|
|
||||||
return <Tooltip key={action.key} placement="top" title={action.name}>
|
|
||||||
<Link to={`${action.path}/1`} style={{padding: '5px 8px'}}>
|
|
||||||
<Icon type='edit' />
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'view':
|
|
||||||
return <Tooltip key={action.key} placement="top" title={action.name}>
|
|
||||||
<Link to={`${action.path}/2`} style={{padding: '5px 8px'}}>
|
|
||||||
<Icon type='right-circle-o' />
|
|
||||||
</Link>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delete':
|
|
||||||
return <Tooltip key={action.key} placement="top" title={action.name}>
|
|
||||||
<Popconfirm
|
|
||||||
title="Are you sure delete this item?"
|
|
||||||
onConfirm={() => this.handleDeleteConfirmYes(item)}
|
|
||||||
onCancel={() => this.handleDeleteConfirmNo(item)}
|
|
||||||
okText="Yes"
|
|
||||||
cancelText="No" >
|
|
||||||
<a href="javascript:;" style={{padding: '5px 8px'}}>
|
|
||||||
<Icon type='delete' />
|
|
||||||
</a>
|
|
||||||
</Popconfirm>
|
|
||||||
</Tooltip>
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
let { columns, keyValue, actions } = this.props;
|
|
||||||
let {
|
|
||||||
sortedInfo,
|
|
||||||
filteredInfo,
|
|
||||||
headerColumns,
|
|
||||||
currentPage,
|
|
||||||
pageSize,
|
|
||||||
totalData,
|
|
||||||
data,
|
|
||||||
searchValue
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
// let headerColumns = columns.map(column => {
|
|
||||||
// if (column.sorter) {
|
|
||||||
// column.sorter = (a, b) => a[column.dataIndex] - b[column.dataIndex];
|
|
||||||
// column.sortOrder =
|
|
||||||
// sortedInfo &&
|
|
||||||
// sortedInfo.columnKey === column.dataIndex &&
|
|
||||||
// sortedInfo.order;
|
|
||||||
// }
|
|
||||||
// return column;
|
|
||||||
// });
|
|
||||||
|
|
||||||
console.log("====================================");
|
|
||||||
console.log(currentPage, pageSize, "TESTASDASD");
|
|
||||||
console.log("====================================");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="table-operations">
|
|
||||||
<Button onClick={this.handleclearAll}>
|
|
||||||
Clear filters
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Search
|
|
||||||
value={searchValue}
|
|
||||||
placeholder="input search text"
|
|
||||||
onSearch={this.handleSearch}
|
|
||||||
onChange={this.handleSearchChange}
|
|
||||||
enterButton
|
|
||||||
/>
|
|
||||||
<Table
|
|
||||||
rowKey={keyValue}
|
|
||||||
dataSource={data}
|
|
||||||
loading={this.state.loading}
|
|
||||||
onChange={this.handleTableChange}
|
|
||||||
pagination={false}
|
|
||||||
>
|
|
||||||
{columns &&
|
|
||||||
columns.map(column => (
|
|
||||||
<Column
|
|
||||||
key={column.dataIndex ? column.dataIndex : column.key}
|
|
||||||
{...column}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{
|
|
||||||
actions && actions.length > 0 && <Column
|
|
||||||
title="Action"
|
|
||||||
align="center"
|
|
||||||
key="action"
|
|
||||||
width={130}
|
|
||||||
render={(text, record) => (
|
|
||||||
|
|
||||||
actions.map(item => {
|
|
||||||
return item.access === true && this.handleRenderActionButton({action: item, item: record})
|
|
||||||
})
|
|
||||||
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Table>
|
|
||||||
<Row style={{paddingTop: 20}}>
|
|
||||||
<Col span={8}>col-12</Col>
|
|
||||||
<Col span={16} style={{textAlign: 'right'}}>
|
|
||||||
<Pagination
|
|
||||||
// size="small"
|
|
||||||
current={currentPage}
|
|
||||||
pageSize={pageSize}
|
|
||||||
showSizeChanger
|
|
||||||
onChange={this.handlePagination}
|
|
||||||
onShowSizeChange={this.handleSizeChange}
|
|
||||||
// defaultCurrent={currentPage}
|
|
||||||
total={totalData}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(CustomTable);
|
|
|
@ -1,90 +0,0 @@
|
||||||
import React, { Component } from "react";
|
|
||||||
import { Icon, Avatar, Dropdown, Menu, notification } from "antd";
|
|
||||||
import { Link, withRouter } from "react-router-dom";
|
|
||||||
import Helmet from 'react-helmet';
|
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { customAction } from 'actions'
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { API_UNI_OIL , API_POST } from 'utils/Api'
|
|
||||||
|
|
||||||
const HeaderButton = styled.a`
|
|
||||||
/* This renders the buttons above... Edit me! */
|
|
||||||
padding: 0 10px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-transition: all .3s,padding 0s;
|
|
||||||
transition: all .3s,padding 0s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: rgb(243, 243, 243);
|
|
||||||
color: #fff
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderDropdown extends Component {
|
|
||||||
state = {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
handleLogout = () => {
|
|
||||||
this.props.customAction({type: 'LOGOUT'});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
//const { userInfo } = this.state
|
|
||||||
const { history, userInfo } = this.props;
|
|
||||||
|
|
||||||
const menu = (
|
|
||||||
<Menu style={{width: 150 , margin: '0 0 0 auto'}} >
|
|
||||||
<Menu.Item key="0">
|
|
||||||
<a
|
|
||||||
onClick={()=> history.push("/my-profile")}
|
|
||||||
role="button"
|
|
||||||
rel="noopener noreferrer" >
|
|
||||||
<Icon type="user" /> My Profile
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Divider />
|
|
||||||
<Menu.Item key="1">
|
|
||||||
<a
|
|
||||||
role="button"
|
|
||||||
onClick={this.handleLogout}>
|
|
||||||
<Icon type="logout" /> Logout
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Helmet title = "Dashboard" />
|
|
||||||
<Dropdown overlay={menu} placement="bottomRight">
|
|
||||||
<HeaderButton role="button" style={{
|
|
||||||
marginRight: 16, color: '#8E8E93', maxWidth: '256px',
|
|
||||||
whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' , position: 'relative'
|
|
||||||
}} >
|
|
||||||
<Avatar size="small"
|
|
||||||
style={{ background: '#B8BBC9', marginRight: 5 }} icon="user"
|
|
||||||
/> { userInfo && (`${userInfo.firstname} ${userInfo.lastname}`) } <Icon type="down" />
|
|
||||||
</HeaderButton>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HeaderDropdown = connect(
|
|
||||||
state => ({
|
|
||||||
//fetchData: state.fetchData
|
|
||||||
// put initial values from account reducer
|
|
||||||
}),
|
|
||||||
{ customAction }
|
|
||||||
)(HeaderDropdown)
|
|
||||||
|
|
||||||
export default withRouter(HeaderDropdown);
|
|
|
@ -1,75 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Breadcrumb, Icon } from 'antd';
|
|
||||||
|
|
||||||
// import styled from 'styled-components';
|
|
||||||
import { Link, withRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
|
|
||||||
function MainBreadcrumbs(props) {
|
|
||||||
const {
|
|
||||||
pageRoutes,
|
|
||||||
// match,
|
|
||||||
location,
|
|
||||||
root
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const pathSnippets = location.pathname.split('/').filter(i => i);
|
|
||||||
const extraBreadcrumbItems = pathSnippets.map((route, index) => {
|
|
||||||
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
|
|
||||||
const routeCompare = pageRoutes.find((myRoute) => myRoute.path === url)
|
|
||||||
const paramsId = pathSnippets[pathSnippets.length - 1]
|
|
||||||
|
|
||||||
if (routeCompare) {
|
|
||||||
if (routeCompare.params) {
|
|
||||||
return (
|
|
||||||
<Breadcrumb.Item key={index}>
|
|
||||||
<Link to={`${url}/${paramsId}`}>
|
|
||||||
{routeCompare.name}
|
|
||||||
</Link>
|
|
||||||
</Breadcrumb.Item>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Breadcrumb.Item key={index}>
|
|
||||||
<Link to={url}>
|
|
||||||
{routeCompare.name}
|
|
||||||
</Link>
|
|
||||||
</Breadcrumb.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return <Breadcrumb.Item key={index}></Breadcrumb.Item>;
|
|
||||||
})
|
|
||||||
if (root) {
|
|
||||||
return (
|
|
||||||
<Breadcrumb
|
|
||||||
separator={<Icon type="right" style={{fontSize: '10px', opacity: 0.6}} />}
|
|
||||||
routeComparestyle={{ padding: '11px 24px 9px', fontSize: '12px' }}
|
|
||||||
>
|
|
||||||
<Breadcrumb.Item>
|
|
||||||
<Link to='/my-profile'>
|
|
||||||
<Icon type="home" /> {` Home`}
|
|
||||||
</Link>
|
|
||||||
</Breadcrumb.Item>
|
|
||||||
</Breadcrumb>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Breadcrumb
|
|
||||||
separator={<Icon type="right" style={{fontSize: '10px', opacity: 0.6}} />}
|
|
||||||
style={{ padding: '11px 24px 9px', fontSize: '12px' }}
|
|
||||||
>
|
|
||||||
<Breadcrumb.Item>
|
|
||||||
<Link to='/my-profile'>
|
|
||||||
<Icon type="home" /> {` Home`}
|
|
||||||
</Link>
|
|
||||||
</Breadcrumb.Item>
|
|
||||||
{extraBreadcrumbItems}
|
|
||||||
</Breadcrumb>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(MainBreadcrumbs);
|
|
|
@ -1,36 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Layout } from 'antd';
|
|
||||||
import { withRouter } from 'react-router-dom'
|
|
||||||
|
|
||||||
import MainBreadcrumbs from './MainBreadcrumbs'
|
|
||||||
const { Content } = Layout;
|
|
||||||
|
|
||||||
function MainContent(props) {
|
|
||||||
const {
|
|
||||||
children,
|
|
||||||
pageRoutes,
|
|
||||||
root
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
[
|
|
||||||
props.location && props.location.key && (
|
|
||||||
<div key={1} style={{
|
|
||||||
background: '#fff' ,
|
|
||||||
marginBottom: '75px',
|
|
||||||
position: 'fixed',
|
|
||||||
marginTop: '-110px',
|
|
||||||
width: '100%'}}
|
|
||||||
>
|
|
||||||
<MainBreadcrumbs root={root} pageRoutes={pageRoutes} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
,
|
|
||||||
<Content key={2} style={{ margin: '0 16px', padding: '0', background: '#fff', }}>
|
|
||||||
{children}
|
|
||||||
</Content>,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(MainContent);
|
|
|
@ -1,19 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Layout } from "antd";
|
|
||||||
|
|
||||||
// import styled from 'styled-components';
|
|
||||||
// import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
const { Footer } = Layout;
|
|
||||||
|
|
||||||
function MainFooter(props) {
|
|
||||||
// const {} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Footer style={{ textAlign: "center", background: '#fcfcfc' }} >
|
|
||||||
{/* YONDU-SDG ©2018 Created by Front End Team! */}
|
|
||||||
</Footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainFooter;
|
|
|
@ -1,83 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Layout, Icon } from "antd";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import HeaderDropdown from './HeaderDropdown';
|
|
||||||
|
|
||||||
const { Header } = Layout;
|
|
||||||
|
|
||||||
|
|
||||||
const HeaderLink = styled(Link)`
|
|
||||||
/* This renders the buttons above... Edit me! */
|
|
||||||
padding: 0 10px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-transition: all .3s,padding 0s;
|
|
||||||
transition: all .3s,padding 0s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #1890ff;
|
|
||||||
color: #fff
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const RightHeader = styled.div`
|
|
||||||
/* This renders the buttons above... Edit me! */
|
|
||||||
float: right;
|
|
||||||
|
|
||||||
|
|
||||||
`
|
|
||||||
|
|
||||||
const IconTrigger = styled(Icon)`
|
|
||||||
/* This renders the buttons above... Edit me! */
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 69px;
|
|
||||||
cursor: pointer;
|
|
||||||
-webkit-transition: all .3s,padding 0s;
|
|
||||||
transition: all .3s,padding 0s;
|
|
||||||
padding: 0 24px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
function MainHeader(props) {
|
|
||||||
const {
|
|
||||||
collapsed,
|
|
||||||
toggle,
|
|
||||||
userInfo,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Header style={{ background: '#fff', padding: 0, height: '66px', lineHeight: '69px', borderBottom: '1px solid rgb(230, 236, 245)', }}>
|
|
||||||
<IconTrigger
|
|
||||||
className="trigger"
|
|
||||||
type={collapsed ? 'menu-unfold' : 'menu-fold'}
|
|
||||||
onClick={toggle}
|
|
||||||
/>
|
|
||||||
<RightHeader>
|
|
||||||
<HeaderDropdown userInfo={userInfo}/>
|
|
||||||
</RightHeader>
|
|
||||||
</Header>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MainHeader = connect(
|
|
||||||
state => ({
|
|
||||||
|
|
||||||
// pull initial values from account reducer
|
|
||||||
}),
|
|
||||||
// { customAction }
|
|
||||||
)(MainHeader);
|
|
||||||
|
|
||||||
export default MainHeader;
|
|
|
@ -1,262 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Layout, Icon, Menu } from 'antd';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
import { withRouter, Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
const { SubMenu } = Menu;
|
|
||||||
const { Sider } = Layout;
|
|
||||||
|
|
||||||
const LogoPlaceholder = styled.div`
|
|
||||||
height: 32px;
|
|
||||||
margin: 16px;
|
|
||||||
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
background-position: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
function MainSidebar(props) {
|
|
||||||
const { collapsed, match, location, userInfo, systemPreferences } = props;
|
|
||||||
|
|
||||||
const navigation = [
|
|
||||||
{
|
|
||||||
key: 0,
|
|
||||||
label: 'User Management',
|
|
||||||
path: '/user-management',
|
|
||||||
icon: 'team',
|
|
||||||
access: userInfo && userInfo.role == 1 ? true : false,
|
|
||||||
//access: userInfo && (userInfo.role == 1 || userInfo.role == 3) ? true : false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 9,
|
|
||||||
label: 'Notifications',
|
|
||||||
path: '/notifications',
|
|
||||||
icon: 'notification',
|
|
||||||
access: true,
|
|
||||||
//access: userInfo && (userInfo.role == 1 || userInfo.role == 3) ? true : false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 4,
|
|
||||||
label: 'Member Management',
|
|
||||||
path: '/member-management',
|
|
||||||
icon: 'credit-card',
|
|
||||||
access: userInfo && userInfo.role == 1 ? true : false,
|
|
||||||
child: [
|
|
||||||
{
|
|
||||||
key: 0.0,
|
|
||||||
label: 'Card Member',
|
|
||||||
path: '/member-management/card-member',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.1,
|
|
||||||
label: 'Locked Accounts',
|
|
||||||
path: '/member-management/lock-account',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 8,
|
|
||||||
label: 'Home Page ( Mobile ) ',
|
|
||||||
path: '/home-page',
|
|
||||||
icon: 'home',
|
|
||||||
access: true,
|
|
||||||
child: [
|
|
||||||
{
|
|
||||||
key: 0.0,
|
|
||||||
label: 'Photo Slider',
|
|
||||||
path: '/home-page/photo-slider',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 3,
|
|
||||||
label: 'Promotions',
|
|
||||||
path: '/promotions',
|
|
||||||
icon: 'tags',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
key: 2,
|
|
||||||
label: 'Top-Up',
|
|
||||||
path: '/top-up',
|
|
||||||
icon: 'plus-circle',
|
|
||||||
access: userInfo && userInfo.role == 1 ? true : false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 6,
|
|
||||||
label: 'About Us',
|
|
||||||
path: '/about-us',
|
|
||||||
icon: 'info-circle',
|
|
||||||
access: userInfo && userInfo.role == 1 ? true : false,
|
|
||||||
child: [
|
|
||||||
{
|
|
||||||
key: 0.6,
|
|
||||||
label: 'Card Types',
|
|
||||||
path: '/about-us/card-types',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.5,
|
|
||||||
label: 'Terms & Privacy',
|
|
||||||
path: '/about-us/term-privacy',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 7,
|
|
||||||
label: 'Reports',
|
|
||||||
path: '/reports',
|
|
||||||
icon: 'file-text',
|
|
||||||
access: true,
|
|
||||||
child: [
|
|
||||||
{
|
|
||||||
key: 0.7,
|
|
||||||
label: 'Registration Report',
|
|
||||||
path: '/reports/registration-report',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.8,
|
|
||||||
label: 'Top-Up Usage Report',
|
|
||||||
path: '/reports/top-up',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.9,
|
|
||||||
label: 'Mobile Usage Report',
|
|
||||||
path: '/reports/mobile-report',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.1,
|
|
||||||
label: 'Station Rating Report',
|
|
||||||
path: '/reports/station-rating',
|
|
||||||
access: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 8,
|
|
||||||
label: 'System Parameters',
|
|
||||||
path: '/system-parameters',
|
|
||||||
icon: 'setting',
|
|
||||||
access: userInfo && userInfo.role == 1 ? true : false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 12,
|
|
||||||
label: 'Station Locator',
|
|
||||||
path:'',
|
|
||||||
icon:'environment',
|
|
||||||
access: true,
|
|
||||||
child: [
|
|
||||||
{
|
|
||||||
key: 0.11,
|
|
||||||
label: 'Branches',
|
|
||||||
path:'/branches',
|
|
||||||
access: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.12,
|
|
||||||
label: 'Stations',
|
|
||||||
path:'/stations',
|
|
||||||
access: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 0.13,
|
|
||||||
label: 'Fuels',
|
|
||||||
path:'/fuels',
|
|
||||||
access: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let newURL = location.pathname.split('/');
|
|
||||||
let appendedUrl = newURL[2];
|
|
||||||
if (appendedUrl == 'create' || appendedUrl == 'view' || appendedUrl == 'edit') appendedUrl = null;
|
|
||||||
let isSeondDaryPathExist = appendedUrl ? `/${appendedUrl}` : '';
|
|
||||||
let secondaryURL = `${match.path}${isSeondDaryPathExist}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Sider
|
|
||||||
trigger={null}
|
|
||||||
collapsible
|
|
||||||
width={295}
|
|
||||||
collapsed={collapsed}
|
|
||||||
style={{ background: '#fff', border: 'solid 1px #e6ecf5', zIndex: '999' }}
|
|
||||||
>
|
|
||||||
{!collapsed ? (
|
|
||||||
<div style={{ height: '65px', padding: '12px 0', textAlign: 'center', borderBottom: '1px solid #e6ecf5' }}>
|
|
||||||
{/* <img src={ require("assets/img/logo_unioil.png") } style={{ height: 40 }} /> */}
|
|
||||||
{userInfo && (
|
|
||||||
<img src={`${systemPreferences ? systemPreferences : userInfo.logo}`} style={{ height: '100%' }} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<LogoPlaceholder
|
|
||||||
className='logo'
|
|
||||||
style={{ backgroundImage: `url(${systemPreferences ? systemPreferences : userInfo.logo})` }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Menu
|
|
||||||
style={{ borderRight: !collapsed ? 0 : null, height: '90vh', overflow: 'auto', paddingTop: '17px' }}
|
|
||||||
//inlineIndent={10}
|
|
||||||
defaultOpenKeys={[ match.path ]}
|
|
||||||
selectedKeys={[ secondaryURL ]}
|
|
||||||
mode='inline'
|
|
||||||
>
|
|
||||||
{navigation.map((item) => {
|
|
||||||
if (item.access) {
|
|
||||||
if (item.child) {
|
|
||||||
return (
|
|
||||||
<SubMenu
|
|
||||||
key={item.path}
|
|
||||||
title={
|
|
||||||
<span>
|
|
||||||
<Icon type={item.icon} />
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{item.child.map((subItem) => {
|
|
||||||
if (subItem.access) {
|
|
||||||
return (
|
|
||||||
<Menu.Item key={subItem.path}>
|
|
||||||
<Link to={subItem.path} style={{ paddingLeft: '15px' }}>
|
|
||||||
{subItem.icon && <Icon type={subItem.icon} />}
|
|
||||||
{subItem.label}
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</SubMenu>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Menu.Item key={item.path}>
|
|
||||||
<Link to={item.path}>
|
|
||||||
{item.icon && <Icon type={item.icon} />}
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</Link>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</Sider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(MainSidebar);
|
|
|
@ -1,107 +0,0 @@
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { Layout } from 'antd';
|
|
||||||
import { notification } from "antd";
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import IdleTimer from 'react-idle-timer'
|
|
||||||
|
|
||||||
import MainFooter from './components/MainFooter'
|
|
||||||
import MainHeader from './components/MainHeader'
|
|
||||||
import MainSidebar from './components/MainSidebar'
|
|
||||||
|
|
||||||
import { API_UNI_OIL , API_POST } from 'utils/Api'
|
|
||||||
import { customAction } from 'actions'
|
|
||||||
|
|
||||||
class DashboardLayout extends React.Component {
|
|
||||||
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
this.idleTimer = null
|
|
||||||
this.onActive = this.handleActive.bind(this)
|
|
||||||
this.onIdle = this.handleIdle.bind(this)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
collapsed: false,
|
|
||||||
userInfo: null,
|
|
||||||
updatedLogo: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nexProps, prevProps) {
|
|
||||||
if(nexProps && nexProps.systemPreferences) {
|
|
||||||
if(nexProps.systemPreferences.data && nexProps.systemPreferences.data) {
|
|
||||||
this.setState({
|
|
||||||
updatedLogo: nexProps.systemPreferences.data.logo
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(nexProps) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
handleActive(e) {
|
|
||||||
// console.log('user is active', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleIdle(e) {
|
|
||||||
// console.log('user is idle', e)
|
|
||||||
notification.error({
|
|
||||||
message: "Error",
|
|
||||||
description: <div>You are logout automatically for being idle more than 10 minutes.</div>,
|
|
||||||
duration: 0,
|
|
||||||
key: 'idle-notification-1'
|
|
||||||
});
|
|
||||||
this.props.customAction({type: 'LOGOUT'});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle = () => {
|
|
||||||
this.setState({
|
|
||||||
collapsed: !this.state.collapsed,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
|
|
||||||
//const { userInfo } = this.state
|
|
||||||
const { children, userInfo } = this.props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout style={{ height: '100%' }}>
|
|
||||||
<MainSidebar collapsed={this.state.collapsed} userInfo={userInfo.data.userInfo} systemPreferences={this.state.updatedLogo}/>
|
|
||||||
<Layout style={{background: '#fcfcfc', paddingBottom: '10px'}}>
|
|
||||||
<MainHeader
|
|
||||||
collapsed={this.state.collapsed}
|
|
||||||
toggle={this.toggle}
|
|
||||||
userInfo={userInfo.data.userInfo}
|
|
||||||
/>
|
|
||||||
<div style={{ overflow: 'auto', marginTop: '94px', paddingTop: '16px', position: 'relative' }}>
|
|
||||||
<IdleTimer
|
|
||||||
ref={ref => { this.idleTimer = ref }}
|
|
||||||
element={document}
|
|
||||||
onActive={this.onActive}
|
|
||||||
onIdle={this.onIdle}
|
|
||||||
timeout={600000}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</IdleTimer>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DashboardLayout = connect(
|
|
||||||
state => ({
|
|
||||||
// pull initial values from account reducer
|
|
||||||
userInfo: state.login,
|
|
||||||
systemPreferences: state.systemPreferences
|
|
||||||
}),
|
|
||||||
{ customAction }
|
|
||||||
)(DashboardLayout);
|
|
||||||
|
|
||||||
export default DashboardLayout
|
|
|
@ -1,31 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import DashboardLayout from '../Layout'
|
|
||||||
import { Route, Redirect } from 'react-router-dom'
|
|
||||||
import { connect } from 'react-redux'
|
|
||||||
|
|
||||||
|
|
||||||
function DashboardRoute({ component: Component, isAuthenticated, ...rest }) {
|
|
||||||
return (
|
|
||||||
<Route {...rest} render={props => isAuthenticated ? (
|
|
||||||
<DashboardLayout>
|
|
||||||
<Component {...props} />
|
|
||||||
</DashboardLayout>
|
|
||||||
) : <Redirect
|
|
||||||
to={{
|
|
||||||
pathname: "/login",
|
|
||||||
state: { from: props.location, message: "You must log in to Enter this page" }
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DashboardRoute = connect(
|
|
||||||
state => ({
|
|
||||||
isAuthenticated: state.auth.isAuthenticated
|
|
||||||
}),
|
|
||||||
)(DashboardRoute);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import React, { Component, Fragment } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { List, Avatar } from 'antd';
|
|
||||||
|
|
||||||
import { fetchData } from 'utils/Api';
|
|
||||||
|
|
||||||
class ListDataDisplay extends Component {
|
|
||||||
state = {
|
|
||||||
data: []
|
|
||||||
};
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
// const { url } = this.props;
|
|
||||||
// const response = await fetchData(url);
|
|
||||||
// this.setState({
|
|
||||||
// data: response.data
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { layout, avatar, viewPath, header, footer } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<List
|
|
||||||
header={header}
|
|
||||||
footer={footer}
|
|
||||||
itemLayout={layout}
|
|
||||||
dataSource={this.state && this.state.data.data}
|
|
||||||
renderItem={item => (
|
|
||||||
<List.Item>
|
|
||||||
<List.Item.Meta
|
|
||||||
avatar={avatar && <Avatar
|
|
||||||
src={item.avatar}
|
|
||||||
/>}
|
|
||||||
title={<Link to={viewPath.replace(':id', item.id)}>{item.first_name}</Link>}
|
|
||||||
description={`${item.first_name.toLowerCase()}_${item.last_name.toLowerCase()}@gmail.com`}
|
|
||||||
/>
|
|
||||||
</List.Item>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default ListDataDisplay;
|
|
|
@ -1,5 +0,0 @@
|
||||||
import List from './List';
|
|
||||||
|
|
||||||
export {
|
|
||||||
List,
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
import React, { PureComponent } from "react";
|
|
||||||
import { withRouter } from "react-router-dom";
|
|
||||||
import { Menu, Dropdown, Button, notification } from "antd";
|
|
||||||
import DownloadFile from "js-file-download";
|
|
||||||
import queryString from "query-string";
|
|
||||||
import moment from 'moment';
|
|
||||||
|
|
||||||
import { API_UNI_OIL } from "utils/Api";
|
|
||||||
|
|
||||||
class DropdownExport extends PureComponent {
|
|
||||||
state = {
|
|
||||||
loading: false
|
|
||||||
}
|
|
||||||
|
|
||||||
handleExportCSV = async() => {
|
|
||||||
this.setState({ loading: true });
|
|
||||||
|
|
||||||
let { location } = this.props;
|
|
||||||
let { search } = location;
|
|
||||||
let params = queryString.parse(search);
|
|
||||||
|
|
||||||
if(this.props.defaultFilter){
|
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
...this.props.defaultFilter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
let response = await API_UNI_OIL.get(this.props.url.path, {
|
|
||||||
params,
|
|
||||||
responseType: 'blob'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 200 || response.status === 201) {
|
|
||||||
//let dateNow = moment(new Date()).format('DD-MMM-YYYY')
|
|
||||||
let dateNow = moment(new Date()).format('MMDDYYYY')
|
|
||||||
DownloadFile(response.data, `${this.props.url.fileName}_${dateNow}.csv`);
|
|
||||||
|
|
||||||
this.setState({ loading: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
this.setState({ loading: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const { loading } = this.state;
|
|
||||||
|
|
||||||
return(
|
|
||||||
<Button
|
|
||||||
loading={loading}
|
|
||||||
onClick={this.handleExportCSV}
|
|
||||||
style={{background: 'rgb(231, 70, 16)', borderColor: 'rgb(231, 70, 16)', color: '#fff'}}
|
|
||||||
>
|
|
||||||
Export CSV
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(DropdownExport);
|
|
|
@ -1 +0,0 @@
|
||||||
export { default as DropdownExport } from "./DropdownExport";
|
|
|
@ -9,7 +9,7 @@ const CheckboxForm = ({
|
||||||
inline,
|
inline,
|
||||||
layout,
|
layout,
|
||||||
required,
|
required,
|
||||||
...props,
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
if (inline) {
|
if (inline) {
|
||||||
|
|
|
@ -40,7 +40,7 @@ class InputForm extends Component {
|
||||||
layout,
|
layout,
|
||||||
optionsList,
|
optionsList,
|
||||||
isRadioButton,
|
isRadioButton,
|
||||||
...props,
|
...props
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let _props = {...props};
|
let _props = {...props};
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
import { Icon } from 'antd';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
const Loading = () => {
|
|
||||||
return (
|
|
||||||
<div style={{padding: 20, display: 'flex' , justifyContent: 'center' , marginLeft: '-144px'}}>
|
|
||||||
<div>
|
|
||||||
<Icon type="sync" spin /> Loading Data Please wait...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Loading;
|
|
|
@ -1,23 +0,0 @@
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
import { Layout } from 'antd';
|
|
||||||
const { Header, Footer, Content, Sider } = Layout;
|
|
||||||
|
|
||||||
|
|
||||||
function LoginLayout({children, ...rest}) {
|
|
||||||
return (
|
|
||||||
<Layout style={{height: "100%"}}>
|
|
||||||
<Sider width='50%' style={{ background: `url(${require("assets/img/bg_cms.png")}) center`, backgroundSize: 'cover' } }></Sider>
|
|
||||||
<Layout>
|
|
||||||
<Content style={{padding: 16}} >{children}</Content>
|
|
||||||
<Footer style={{textAlign: 'center', fontSize: '12px'}}>
|
|
||||||
<div style={{margin: '25px auto', padding: '17px 0', width: '325px', borderTop: '1px solid #e0e0e0', textAlign: 'left', color: '#8E8E93' }}>
|
|
||||||
By logging in you agree to Unioil's Terms of Service, <br/>Privacy Policy and Content Policies.
|
|
||||||
</div>
|
|
||||||
</Footer>
|
|
||||||
</Layout>
|
|
||||||
</Layout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginLayout
|
|
|
@ -1,16 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import LoginLayout from '../Layout'
|
|
||||||
import { Route } from 'react-router-dom'
|
|
||||||
const LoginLayoutRoute = ({component: Component, ...rest}) => {
|
|
||||||
return (
|
|
||||||
<Route {...rest} render={matchProps => (
|
|
||||||
<LoginLayout>
|
|
||||||
<Component {...matchProps} />
|
|
||||||
</LoginLayout>
|
|
||||||
)} />
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default LoginLayoutRoute
|
|
|
@ -1,84 +0,0 @@
|
||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import { Modal, Button } from "antd";
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
class ModalCancel extends React.Component {
|
|
||||||
state = { visible: false };
|
|
||||||
|
|
||||||
showModal = () => {
|
|
||||||
this.setState({
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleOk = e => {
|
|
||||||
console.log(e);
|
|
||||||
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCancel = e => {
|
|
||||||
console.log(e);
|
|
||||||
this.setState({
|
|
||||||
visible: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
const {
|
|
||||||
path,
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
id,
|
|
||||||
dirty,
|
|
||||||
name,
|
|
||||||
loading
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
if (dirty) {
|
|
||||||
return [
|
|
||||||
<Button
|
|
||||||
key="button"
|
|
||||||
disabled={loading}
|
|
||||||
onClick={this.showModal}>
|
|
||||||
{name}
|
|
||||||
</Button>,
|
|
||||||
<Modal
|
|
||||||
getContainer={() => document.getElementById(id)}
|
|
||||||
key="modal"
|
|
||||||
width={300}
|
|
||||||
title={title ? title : "Your Title"}
|
|
||||||
visible={this.state.visible}
|
|
||||||
onOk={this.handleOk}
|
|
||||||
onCancel={this.handleCancel}
|
|
||||||
footer={[
|
|
||||||
<Button key={1} type="primary" onClick={this.handleCancel}>No</Button>,
|
|
||||||
<Link key={2} to={path} style={{marginLeft: 10}}>
|
|
||||||
<Button >
|
|
||||||
Yes
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{message ? message : "Your Message"}
|
|
||||||
</Modal>
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return <Link to={path} disabled={loading}>
|
|
||||||
<Button key="back" disabled={loading}>
|
|
||||||
{name}
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ModalCancel;
|
|
|
@ -1,429 +0,0 @@
|
||||||
import React, { Component } from 'react';
|
|
||||||
import { withRouter } from 'react-router-dom';
|
|
||||||
import queryString from 'query-string';
|
|
||||||
import { getCookie } from '../../utils/cookie';
|
|
||||||
|
|
||||||
import _ from 'lodash';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
Button,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Input,
|
|
||||||
Icon,
|
|
||||||
Pagination,
|
|
||||||
Tooltip,
|
|
||||||
notification,
|
|
||||||
Popconfirm,
|
|
||||||
message,
|
|
||||||
DatePicker,
|
|
||||||
} from 'antd';
|
|
||||||
|
|
||||||
import { DropdownExport } from 'components/Dropdown/index';
|
|
||||||
import { fnQueryParams } from 'utils/helper';
|
|
||||||
import { API_UNI_OIL, API_GET, API_DELETE } from 'utils/Api';
|
|
||||||
import { API_GET_NOTIF } from 'utils/NotificationApi';
|
|
||||||
import '../Tables/index.css';
|
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
|
||||||
|
|
||||||
class Index extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
data: null,
|
|
||||||
total: null,
|
|
||||||
loading: false,
|
|
||||||
selectedRowKeys: [],
|
|
||||||
columns: [],
|
|
||||||
search_filter: '',
|
|
||||||
visible: false,
|
|
||||||
mounted: false,
|
|
||||||
test: true,
|
|
||||||
updating: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.delayFetchRequest = _.debounce(this.fetch, 500);
|
|
||||||
this.handleSearchChangeDebounce = _.debounce(this.handleSearchStateChange, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.setState({ mounted: true });
|
|
||||||
this.handleFilterChange({});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
|
||||||
if (prevState.updating !== prevProps.updating) {
|
|
||||||
this.setState({ updating: prevProps.updating });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
|
||||||
if (nextProps.updating !== nextState.updating) {
|
|
||||||
this.handleFilterChange({});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.delayFetchRequest.cancel();
|
|
||||||
this.handleSearchChangeDebounce.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleTableChange = (pagination, filters, sorter) => {
|
|
||||||
let _sort_order;
|
|
||||||
if (sorter.order) _sort_order = sorter.order === 'ascend' ? 'asc' : 'desc';
|
|
||||||
|
|
||||||
if (sorter.column) {
|
|
||||||
if (sorter.column.sortByValue) sorter.field = sorter.column.sortByValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleFilterChange({
|
|
||||||
...filters,
|
|
||||||
_sort_by: sorter.field,
|
|
||||||
_sort_order,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSearchChange = (e) => {
|
|
||||||
this.setState({ search_filter: e.target.value });
|
|
||||||
this.handleSearchChangeDebounce(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSearchStateChange = (search_filter) => {
|
|
||||||
this.setState({ search_filter });
|
|
||||||
this.handleFilterChange({ search: this.state.search_filter });
|
|
||||||
};
|
|
||||||
|
|
||||||
onPaginationChange = (page, page_size) => {
|
|
||||||
console.log("page")
|
|
||||||
console.log(page)
|
|
||||||
this.handleFilterChange({ page });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleFilterChange = (props, isClearFilter) => {
|
|
||||||
console.log(props)
|
|
||||||
this.setState({ loading: true });
|
|
||||||
|
|
||||||
let { history, location } = this.props;
|
|
||||||
let { search, pathname } = location;
|
|
||||||
let urlParamsObject = isClearFilter ? props : queryString.parse(search);
|
|
||||||
urlParamsObject = props ? { ...urlParamsObject,page: 1, ...props } : {};
|
|
||||||
urlParamsObject = fnQueryParams(urlParamsObject);
|
|
||||||
urlParamsObject = queryString.parse(urlParamsObject);
|
|
||||||
history.push({ pathname, search: fnQueryParams(urlParamsObject) });
|
|
||||||
console.log({ pathname, search: fnQueryParams(urlParamsObject) })
|
|
||||||
this.delayFetchRequest(urlParamsObject);
|
|
||||||
};
|
|
||||||
|
|
||||||
clearFilters = () => {
|
|
||||||
let { history, location } = this.props;
|
|
||||||
let { search, pathname } = location;
|
|
||||||
let urlParamsObject = queryString.parse(search);
|
|
||||||
delete urlParamsObject['search'];
|
|
||||||
Object.keys(urlParamsObject).map((key, index) => {
|
|
||||||
if (this.props.filterValues.includes(key)) delete urlParamsObject[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
history.push({ pathname, search: fnQueryParams(urlParamsObject) });
|
|
||||||
this.handleFilterChange(urlParamsObject, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
clearAll = () => {
|
|
||||||
this.setState({ search_filter: '' });
|
|
||||||
this.handleFilterChange();
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch = async (params = {}) => {
|
|
||||||
let defaulUrl;
|
|
||||||
|
|
||||||
if (this.props.defaultFilter) {
|
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
...this.props.defaultFilter,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.url.defaultWithFilter) {
|
|
||||||
defaulUrl = this.props.url.defaultWithFilter;
|
|
||||||
} else {
|
|
||||||
defaulUrl = this.props.url.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let response, data, total;
|
|
||||||
if(defaulUrl == 'notification'){
|
|
||||||
console.log(defaulUrl, params)
|
|
||||||
response = await API_GET_NOTIF('notification', params);
|
|
||||||
console.log(response.data, params, 'response');
|
|
||||||
console.log(getCookie('TOKEN').token);
|
|
||||||
data = response.data.data.length > 0 ? response.data.data : null;
|
|
||||||
total = response.data.total
|
|
||||||
}
|
|
||||||
console.table(data, 'data');
|
|
||||||
this.setState({ data, total, loading: false });
|
|
||||||
if (data == null && this.props.isEmptyMessagePopUp) {
|
|
||||||
message.info('No records found.');
|
|
||||||
}
|
|
||||||
if (this.props.dataResponse) {
|
|
||||||
this.props.dataResponse(data.length);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.setState({ loading: false, total: 0 });
|
|
||||||
console.log('An error encountered: ' + error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
update = async (params = {}) => {
|
|
||||||
notification.success({
|
|
||||||
message: 'Success',
|
|
||||||
description: `Delete Successful.`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
remove = async (params = {}) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: `Error message.`,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
delete = async (uuid) => {
|
|
||||||
console.log(uuid)
|
|
||||||
let search = this.props.location;
|
|
||||||
console.log(search.pathname)
|
|
||||||
let api = process.env.REACT_APP_STATION_API
|
|
||||||
let path = search.pathname.substring(1)
|
|
||||||
try {
|
|
||||||
await API_UNI_OIL.delete(`${api}${path}/${uuid}`);
|
|
||||||
this.handleFilterChange({});
|
|
||||||
message.success('Record was successfully deleted.');
|
|
||||||
} catch ({ response: error }) {
|
|
||||||
this.handleFilterChange({});
|
|
||||||
notification.error({
|
|
||||||
message: 'Something went wrong deleting record!',
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<h3>
|
|
||||||
{error && error.data && error.data.message}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
duration: 4,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleBatchDelete = async () => {
|
|
||||||
const data = { [this.props.keyValue]: this.state.selectedRowKeys };
|
|
||||||
this.setState({ selectedRowKeys: [] });
|
|
||||||
|
|
||||||
try {
|
|
||||||
// await API_UNI_OIL.delete(this.props.url.apiDelete, { data });
|
|
||||||
// this.handleFilterChange({});
|
|
||||||
// message.success('Record was successfully deleted.');
|
|
||||||
console.log(this.props.url.apiDelete)
|
|
||||||
} catch ({ response: error }) {
|
|
||||||
this.handleFilterChange({});
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<div>Something went wrong deleting records.</div>
|
|
||||||
- {error && error.data && error.data.message}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
duration: 3,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onSelectChange = (selectedRowKeys) => {
|
|
||||||
this.setState({ selectedRowKeys });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDateRangePicker = async (date, dateString) => {
|
|
||||||
this.handleFilterChange({
|
|
||||||
date_start: dateString[0],
|
|
||||||
date_end: dateString[1],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (!this.state.mounted) return null;
|
|
||||||
const { loading, selectedRowKeys } = this.state;
|
|
||||||
const rowSelection = {
|
|
||||||
selectedRowKeys,
|
|
||||||
onChange: this.onSelectChange,
|
|
||||||
getCheckboxProps: (record) => ({
|
|
||||||
disabled: record.editable == false, // Column configuration not to be checked
|
|
||||||
//name: record.name,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
const hasSelected = selectedRowKeys.length > 0;
|
|
||||||
|
|
||||||
let { history, keyValue, location, url: { apiDelete } } = this.props;
|
|
||||||
|
|
||||||
let { search } = this.props.location;
|
|
||||||
let urlParamsObject = queryString.parse(search);
|
|
||||||
let { _sort_order } = urlParamsObject;
|
|
||||||
if (_sort_order) _sort_order = _sort_order === 'asc' ? 'ascend' : 'descend';
|
|
||||||
|
|
||||||
const columns = this.props.columns.map((data) => {
|
|
||||||
if (data.dataIndex === 'action') {
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
render: (text, record) =>
|
|
||||||
data.buttons.map((action) => {
|
|
||||||
let actionBtn;
|
|
||||||
if(action.key == 'location'){
|
|
||||||
actionBtn = () => this.props.locationData(record)
|
|
||||||
}
|
|
||||||
if(action.key == 'edit'){
|
|
||||||
actionBtn = () => history.push({ pathname: `${location.pathname}/view/${record.id}` });
|
|
||||||
}
|
|
||||||
if (action.key == 'delete') {
|
|
||||||
actionBtn = action.action;
|
|
||||||
if (record.editable == false) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Popconfirm
|
|
||||||
placement='bottomRight'
|
|
||||||
key={action.key}
|
|
||||||
title={'Are you sure you want to delete this record?'}
|
|
||||||
onConfirm={() => this.delete(record.id)}
|
|
||||||
okText='Yes'
|
|
||||||
cancelText='No'
|
|
||||||
icon={<Icon type='close-circle' />}
|
|
||||||
>
|
|
||||||
<Tooltip key={action.key} placement='top' title={action.title}>
|
|
||||||
<Icon
|
|
||||||
type={action.icon}
|
|
||||||
style={{
|
|
||||||
padding: '5px 14px 5px 0',
|
|
||||||
color: 'rgb(231, 70, 16)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</Popconfirm>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip key={action.key} placement='top' title={action.title}>
|
|
||||||
<Icon
|
|
||||||
type={action.icon}
|
|
||||||
style={{
|
|
||||||
padding: '5px 14px 5px 0',
|
|
||||||
color: 'rgb(231, 70, 16)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
onClick={actionBtn}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let filteredValue = null;
|
|
||||||
if (Array.isArray(urlParamsObject[data.dataIndex])) {
|
|
||||||
filteredValue = urlParamsObject[data.dataIndex];
|
|
||||||
} else if (urlParamsObject[data.dataIndex]) {
|
|
||||||
filteredValue = [ urlParamsObject[data.dataIndex] ];
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
filteredValue,
|
|
||||||
sortOrder: data.sorter ? urlParamsObject._sort_by === data.dataIndex && _sort_order : null,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ margin: '0 24px', padding: '24px 0' }}>
|
|
||||||
<Row type='flex' justify='space-between' align='bottom' style={{ paddingBottom: 25 }}>
|
|
||||||
<Col>
|
|
||||||
{this.props.url.csv ? (
|
|
||||||
<RangePicker onChange={this.handleDateRangePicker} />
|
|
||||||
) : (
|
|
||||||
<Input
|
|
||||||
onChange={this.handleSearchChange}
|
|
||||||
style={{ width: 300 }}
|
|
||||||
value={this.state.search_filter}
|
|
||||||
prefix={<Icon type='search' style={{ color: 'rgba(0,0,0,.25)' }} />}
|
|
||||||
type='text'
|
|
||||||
placeholder='Search'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
<Col className='table-operations'>
|
|
||||||
{/* <Button onClick = {this.clearFilters}><b>Clear filters</b></Button>*/}
|
|
||||||
<Button onClick={this.clearAll}>Clear filters</Button>
|
|
||||||
{this.props.url.csv && <DropdownExport defaultFilter={this.props.defaultFilter} url={this.props.url.csv} />}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
<Table
|
|
||||||
size='middle'
|
|
||||||
rowSelection={apiDelete && rowSelection}
|
|
||||||
columns={columns}
|
|
||||||
dataSource={this.state.data ? this.state.data : null}
|
|
||||||
pagination={false}
|
|
||||||
rowKey={(record) => record[this.props.keyValue]}
|
|
||||||
onChange={this.handleTableChange}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Row type='flex' justify='space-between' style={{ marginTop: 20 }}>
|
|
||||||
<Col>
|
|
||||||
{apiDelete && (
|
|
||||||
<div>
|
|
||||||
<Popconfirm
|
|
||||||
placement='top'
|
|
||||||
title={'Are you sure you want to delete this record?'}
|
|
||||||
onConfirm={this.handleBatchDelete}
|
|
||||||
okText='Yes'
|
|
||||||
cancelText='No'
|
|
||||||
icon={<Icon type='close-circle' />}
|
|
||||||
>
|
|
||||||
<Button type='danger' disabled={!hasSelected} icon='delete' loading={loading}>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
<span style={{ marginLeft: 8 }}>
|
|
||||||
{hasSelected ? `Selected ${selectedRowKeys.length} item(s)` : ''}
|
|
||||||
</span>
|
|
||||||
</Popconfirm>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col>
|
|
||||||
{this.state.total > 0 ? (
|
|
||||||
<Pagination
|
|
||||||
style={{ float: 'right' }}
|
|
||||||
showSizeChanger
|
|
||||||
defaultCurrent={parseInt(urlParamsObject.page, 10) || 1}
|
|
||||||
defaultPageSize={parseInt(urlParamsObject.page_size, 10) || 10}
|
|
||||||
pageSizeOptions={[ '10','20']}
|
|
||||||
total={this.state.total}
|
|
||||||
showTotal={(total, range) =>
|
|
||||||
`Showing ${this.state.total > 0 ? range[0] : 0}-${this.state.total > 0 ? range[1] : 0} of ${this.state
|
|
||||||
.total > 0
|
|
||||||
? total
|
|
||||||
: 0}`}
|
|
||||||
onChange={this.onPaginationChange}
|
|
||||||
onShowSizeChange={this.onPaginationChange}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(Index);
|
|
|
@ -1,17 +0,0 @@
|
||||||
import React, { Fragment } from 'react';
|
|
||||||
import { withRouter} from 'react-router-dom'
|
|
||||||
|
|
||||||
function Page404(){
|
|
||||||
const error = "Page not found";
|
|
||||||
const errorMessage = "Sorry, but the page you are looking for doesn't exist";
|
|
||||||
|
|
||||||
return <Fragment>
|
|
||||||
<div align="center" style={{ marginTop: "10%" }}>
|
|
||||||
<h1 style={{ fontSize: 150, fontWeight: 'bold', margin: 0 }}>404</h1>
|
|
||||||
<p style={{ fontSize: 30, fontWeight: 'bold', margin: 0 }}>{error}</p>
|
|
||||||
<p>{errorMessage}</p>
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(Page404);
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -6,12 +6,15 @@
|
||||||
"dev": "vite"
|
"dev": "vite"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.20",
|
"@popperjs/core": "^2.11.6",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
"axios": "^1.7.4",
|
"axios": "^1.7.4",
|
||||||
|
"bootstrap": "^5.2.3",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^1.2.0",
|
"laravel-vite-plugin": "^1.2.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.5.3",
|
||||||
"tailwindcss": "^3.4.13",
|
"sass": "^1.56.1",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
"vite": "^6.0.11"
|
"vite": "^6.0.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,34 @@
|
||||||
|
import 'bootstrap';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||||
|
* to our Laravel back-end. This library automatically handles sending the
|
||||||
|
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||||
|
*/
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
window.axios = axios;
|
window.axios = axios;
|
||||||
|
|
||||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Echo exposes an expressive API for subscribing to channels and listening
|
||||||
|
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||||
|
* allows your team to easily build robust real-time web applications.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// import Echo from 'laravel-echo';
|
||||||
|
|
||||||
|
// import Pusher from 'pusher-js';
|
||||||
|
// window.Pusher = Pusher;
|
||||||
|
|
||||||
|
// window.Echo = new Echo({
|
||||||
|
// broadcaster: 'pusher',
|
||||||
|
// key: import.meta.env.VITE_PUSHER_APP_KEY,
|
||||||
|
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
|
||||||
|
// wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
|
||||||
|
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
|
||||||
|
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
|
||||||
|
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
|
||||||
|
// enabledTransports: ['ws', 'wss'],
|
||||||
|
// });
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Body
|
||||||
|
$body-bg: #f8fafc;
|
||||||
|
|
||||||
|
// Typography
|
||||||
|
$font-family-sans-serif: 'Nunito', sans-serif;
|
||||||
|
$font-size-base: 0.9rem;
|
||||||
|
$line-height-base: 1.6;
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Fonts
|
||||||
|
@import url('https://fonts.bunny.net/css?family=Nunito');
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
@import 'variables';
|
||||||
|
|
||||||
|
// Bootstrap
|
||||||
|
@import 'bootstrap/scss/bootstrap';
|
|
@ -0,0 +1,73 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Login') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="{{ route('login') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
|
||||||
|
|
||||||
|
@error('email')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6 offset-md-4">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
|
||||||
|
|
||||||
|
<label class="form-check-label" for="remember">
|
||||||
|
{{ __('Remember Me') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-0">
|
||||||
|
<div class="col-md-8 offset-md-4">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ __('Login') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@if (Route::has('password.request'))
|
||||||
|
<a class="btn btn-link" href="{{ route('password.request') }}">
|
||||||
|
{{ __('Forgot Your Password?') }}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,49 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Confirm Password') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
{{ __('Please confirm your password before continuing.') }}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('password.confirm') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-0">
|
||||||
|
<div class="col-md-8 offset-md-4">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ __('Confirm Password') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@if (Route::has('password.request'))
|
||||||
|
<a class="btn btn-link" href="{{ route('password.request') }}">
|
||||||
|
{{ __('Forgot Your Password?') }}
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,47 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Reset Password') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('status') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('password.email') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>
|
||||||
|
|
||||||
|
@error('email')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-0">
|
||||||
|
<div class="col-md-6 offset-md-4">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ __('Send Password Reset Link') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,65 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Reset Password') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="{{ route('password.update') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<input type="hidden" name="token" value="{{ $token }}">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus>
|
||||||
|
|
||||||
|
@error('email')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-0">
|
||||||
|
<div class="col-md-6 offset-md-4">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ __('Reset Password') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,77 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Register') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="{{ route('register') }}">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="name" class="col-md-4 col-form-label text-md-end">{{ __('Name') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
|
||||||
|
|
||||||
|
@error('name')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="email" class="col-md-4 col-form-label text-md-end">{{ __('Email Address') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">
|
||||||
|
|
||||||
|
@error('email')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">
|
||||||
|
|
||||||
|
@error('password')
|
||||||
|
<span class="invalid-feedback" role="alert">
|
||||||
|
<strong>{{ $message }}</strong>
|
||||||
|
</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<label for="password-confirm" class="col-md-4 col-form-label text-md-end">{{ __('Confirm Password') }}</label>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-0">
|
||||||
|
<div class="col-md-6 offset-md-4">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ __('Register') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,28 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Verify Your Email Address') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
@if (session('resent'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ __('A fresh verification link has been sent to your email address.') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{ __('Before proceeding, please check your email for a verification link.') }}
|
||||||
|
{{ __('If you did not receive the email') }},
|
||||||
|
<form class="d-inline" method="POST" action="{{ route('verification.resend') }}">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-link p-0 m-0 align-baseline">{{ __('click here to request another') }}</button>.
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,55 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
@if (session('success'))
|
||||||
|
<div class="text-green-500 mb-4">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
<div class="flex justify-between mb-4">
|
||||||
|
<form method="GET" action="{{ route('custom-table.index') }}" class="mb-4">
|
||||||
|
<input type="text" name="search" value="{{ request()->input('search') }}" placeholder="Search..." class="border p-2 rounded" onkeyup="this.form.submit()">
|
||||||
|
</form>
|
||||||
|
<button onclick="window.location.href='{{ route('custom-table.index') }}?page=1'" class="bg-blue-500 text-white p-2 rounded">Clear Filters</button>
|
||||||
|
</div>
|
||||||
|
<table class="w-full border-collapse border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
@foreach ($columns as $column)
|
||||||
|
<th class="border p-2">
|
||||||
|
{{ $column['title'] }}
|
||||||
|
<a href="{{ route('custom-table.index') }}?sort_field={{ $column['dataIndex'] }}&sort_order={{ request()->input('sort_field') === $column['dataIndex'] && request()->input('sort_order') === 'asc' ? 'desc' : 'asc' }}" class="text-blue-500 ml-2">
|
||||||
|
{{ request()->input('sort_field') === $column['dataIndex'] && request()->input('sort_order') === 'asc' ? '↓' : '↑' }}
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
@endforeach
|
||||||
|
<th class="border p-2">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($paginatedData as $item)
|
||||||
|
<tr>
|
||||||
|
@foreach ($columns as $column)
|
||||||
|
<td class="border p-2">{{ $item[$column['dataIndex']] }}</td>
|
||||||
|
@endforeach
|
||||||
|
<td class="border p-2">
|
||||||
|
@foreach ($actions as $action)
|
||||||
|
@if ($action['access'])
|
||||||
|
@if ($action['type'] === 'edit')
|
||||||
|
<a href="{{ $action['path'] }}/{{ $item['id'] }}" class="text-blue-500 mr-2">Edit</a>
|
||||||
|
@elseif ($action['type'] === 'delete')
|
||||||
|
<form action="{{ route('custom-table.destroy', $item['id']) }}" method="POST" class="inline">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<button type="submit" class="text-red-500" onclick="return confirm('Are you sure?')">Delete</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ $paginatedData->links() }}
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,11 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('dashboard.header')
|
||||||
|
@include('dashboard.content')
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content-body')
|
||||||
|
<h2>Dashboard Content</h2>
|
||||||
|
<p>This is the main content area.</p>
|
||||||
|
@endsection
|
|
@ -0,0 +1,15 @@
|
||||||
|
<div style="padding: 11px 24px 9px; font-size: 12px;">
|
||||||
|
<ol style="list-style: none; display: flex; align-items: center; padding: 0;">
|
||||||
|
<li style="margin-right: 8px;">
|
||||||
|
<a href="{{ route('my-profile') }}" style="color: #000;">
|
||||||
|
<span style="font-size: 10px; opacity: 0.6;">▸</span> Home
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@foreach ($extraBreadcrumbItems as $item)
|
||||||
|
<li style="margin-right: 8px;">
|
||||||
|
<span style="font-size: 10px; opacity: 0.6;">▸</span>
|
||||||
|
<a href="{{ $item['url'] }}" style="color: #000;">{{ $item['name'] }}</a>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ol>
|
||||||
|
</div>
|
|
@ -0,0 +1,12 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@if (request()->path() && request()->path() !== '/')
|
||||||
|
<div style="background: #fff; margin-bottom: 75px; position: fixed; margin-top: -110px; width: 100%;">
|
||||||
|
@include('dashboard.breadcrumbs')
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<div style="margin: 0 16px; padding: 0; background: #fff;">
|
||||||
|
@yield('content-body')
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,17 @@
|
||||||
|
@extends('dashboard.route')
|
||||||
|
|
||||||
|
@section('route-content')
|
||||||
|
<h2>{{ $header }}</h2>
|
||||||
|
<ul style="@if($layout === 'horizontal') display: flex; flex-wrap: wrap; @else list-style-type: none; padding: 0; @endif">
|
||||||
|
@foreach ($data as $item)
|
||||||
|
<li style="margin: 10px; @if($layout === 'horizontal') width: 200px; @endif">
|
||||||
|
@if ($avatar)
|
||||||
|
<img src="{{ asset($item['avatar'] ?? '/images/default-avatar.png') }}" alt="Avatar" style="width: 40px; height: 40px; border-radius: 50%; margin-right: 10px;" />
|
||||||
|
@endif
|
||||||
|
<a href="{{ route('view', $item['id']) }}">{{ $item['first_name'] ?? $item['name'] }}</a>
|
||||||
|
<p>{{ strtolower($item['first_name'] ?? $item['name']) }}_{{ strtolower($item['last_name'] ?? '') }}@gmail.com</p>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<div style="text-align: right;">{{ $footer }}</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div style="text-align: center; background: #fcfcfc; padding: 10px;">
|
||||||
|
<!-- YONDU-SDG ©2018 Created by Front End Team! -->
|
||||||
|
</div>
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Dashboard Header</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.header {
|
||||||
|
background: #fff;
|
||||||
|
padding: 0;
|
||||||
|
height: 66px;
|
||||||
|
line-height: 69px;
|
||||||
|
border-bottom: 1px solid rgb(230, 236, 245);
|
||||||
|
}
|
||||||
|
.header-link {
|
||||||
|
padding: 0 10px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s, padding 0s;
|
||||||
|
}
|
||||||
|
.header-link:hover {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.right-header { float: right; }
|
||||||
|
.icon-trigger {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 69px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s, padding 0s;
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
.icon-trigger:hover { color: #1890ff; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@if (session('success'))
|
||||||
|
<div style="color: green;">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="header">
|
||||||
|
<span class="icon-trigger {{ $collapsed ? 'menu-unfold' : 'menu-fold' }}" onclick="toggleSidebar()">
|
||||||
|
☰ <!-- Hamburger icon -->
|
||||||
|
</span>
|
||||||
|
<div class="right-header">
|
||||||
|
@include('dashboard.header-dropdown')
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function toggleSidebar() {
|
||||||
|
fetch('/dashboard/toggle', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ collapsed: !{{ $collapsed }} })
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
location.reload(); // Reload to reflect collapsed state
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,18 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div style="height: 100%;">
|
||||||
|
@include('dashboard.sidebar')
|
||||||
|
<div style="background: #fcfcfc; padding-bottom: 10px;">
|
||||||
|
@include('dashboard.header')
|
||||||
|
<div style="overflow: auto; margin-top: 94px; padding-top: 16px; position: relative;">
|
||||||
|
@include('dashboard.content')
|
||||||
|
@include('dashboard.footer')
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content-body')
|
||||||
|
@yield('content-body')
|
||||||
|
@endsection
|
|
@ -0,0 +1,15 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('dashboard.layout')
|
||||||
|
@if (Auth::check())
|
||||||
|
@include($view)
|
||||||
|
@else
|
||||||
|
<div>{{ session('error') ?? 'You must log in to enter this page.' }}</div>
|
||||||
|
<a href="{{ route('login') }}">Login</a>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content-body')
|
||||||
|
@yield('route-content')
|
||||||
|
@endsection
|
|
@ -0,0 +1,45 @@
|
||||||
|
<div style="background: #fff; border: solid 1px #e6ecf5; z-index: 999; height: 100%;">
|
||||||
|
@if (!$collapsed)
|
||||||
|
<div style="height: 65px; padding: 12px 0; text-align: center; border-bottom: 1px solid #e6ecf5;">
|
||||||
|
<!-- Replace with actual logo image -->
|
||||||
|
<img src="{{ asset('images/logo.png') }}" style="height: 40px;" alt="Logo" />
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div style="height: 32px; margin: 16px; background-repeat: no-repeat; background-size: contain; background-position: center; background-image: url('{{ asset('images/logo.png') }}');"></div>
|
||||||
|
@endif
|
||||||
|
<ul style="border-right: {{ $collapsed ? '0' : 'none' }}; height: 90vh; overflow: auto; padding-top: 17px; list-style: none; padding: 0;">
|
||||||
|
@foreach ($navigation as $item)
|
||||||
|
@if ($item['access'])
|
||||||
|
@if (isset($item['child']))
|
||||||
|
<li style="margin-bottom: 10px;">
|
||||||
|
<details>
|
||||||
|
<summary style="padding: 0 24px; cursor: pointer;">
|
||||||
|
<span style="margin-right: 8px;">{{ $item['icon'] ? '<i class="anticon anticon-' . $item['icon'] . '"></i>' : '' }}</span>
|
||||||
|
<span>{{ $item['label'] }}</span>
|
||||||
|
</summary>
|
||||||
|
<ul style="padding-left: 24px;">
|
||||||
|
@foreach ($item['child'] as $subItem)
|
||||||
|
@if ($subItem['access'])
|
||||||
|
<li style="margin-bottom: 5px;">
|
||||||
|
<a href="{{ $subItem['path'] }}" style="padding-left: 15px; display: block;">
|
||||||
|
{{ $subItem['icon'] ? '<i class="anticon anticon-' . $subItem['icon'] . '"></i>' : '' }}
|
||||||
|
{{ $subItem['label'] }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li style="margin-bottom: 10px;">
|
||||||
|
<a href="{{ $item['path'] }}" style="padding: 0 24px; display: block;">
|
||||||
|
{{ $item['icon'] ? '<i class="anticon anticon-' . $item['icon'] . '"></i>' : '' }}
|
||||||
|
<span>{{ $item['label'] }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
|
@ -0,0 +1,5 @@
|
||||||
|
@extends('dashboard.route')
|
||||||
|
|
||||||
|
@section('route-content')
|
||||||
|
<h2>Users Page</h2>
|
||||||
|
@endsection
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Export</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.export-button {
|
||||||
|
background: rgb(231, 70, 16);
|
||||||
|
border-color: rgb(231, 70, 16);
|
||||||
|
color: #fff;
|
||||||
|
padding: 0 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.export-button:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="{{ route('dropdown.export') }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="export-button">Export CSV</button>
|
||||||
|
</form>
|
||||||
|
@if (session('success'))
|
||||||
|
<div style="color: green;">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,23 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">{{ __('Dashboard') }}</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
@if (session('status'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('status') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{ __('You are logged in!') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title inertia>@yield('title', 'Unioil CMS')</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
@vite('resources/css/app.css')
|
||||||
|
</head>
|
||||||
|
<body class="font-sans antialiased">
|
||||||
|
<div class="min-h-screen bg-gray-100">
|
||||||
|
@yield('content')
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div style="padding: 20px; display: flex; justify-content: center; margin-left: -144px;">
|
||||||
|
<div>
|
||||||
|
<span style="animation: spin 1s linear infinite;">⏳</span> Loading Data Please wait...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
span { font-size: 24px; }
|
||||||
|
</style>
|
|
@ -0,0 +1,9 @@
|
||||||
|
@section('content')
|
||||||
|
<h2>Login</h2>
|
||||||
|
<form method="POST" action="{{ route('login') }}">
|
||||||
|
@csrf
|
||||||
|
<input type="email" name="email" placeholder="Email" required>
|
||||||
|
<input type="password" name="password" placeholder="Password" required>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
@endsection
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Login Layout</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.login-layout { height: 100%; display: flex; }
|
||||||
|
.sider { width: 50%; background: url('{{ asset('images/bg_cms.png') }}') center; background-size: cover; }
|
||||||
|
.content { padding: 16px; }
|
||||||
|
.footer { text-align: center; font-size: 12px; }
|
||||||
|
.footer div { margin: 25px auto; padding: 17px 0; width: 325px; border-top: 1px solid #e0e0e0; text-align: left; color: #8E8E93; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-layout">
|
||||||
|
<div class="sider"></div>
|
||||||
|
<div>
|
||||||
|
<div class="content">
|
||||||
|
@yield('content')
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<div>
|
||||||
|
By logging in you agree to Unioil's Terms of Service, <br/>Privacy Policy and Content Policies.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,15 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
@include('dashboard.layout')
|
||||||
|
@if (Auth::check())
|
||||||
|
@include($view)
|
||||||
|
@else
|
||||||
|
<div class="text-red-500">{{ session('error') ?? 'You must log in to enter this page.' }}</div>
|
||||||
|
<a href="{{ route('login') }}" class="text-blue-500">Login</a>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content-body')
|
||||||
|
@yield('route-content')
|
||||||
|
@endsection
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Modal Cancel</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@if ($dirty)
|
||||||
|
<button onclick="document.getElementById('modal').style.display='block'">{{ $name }}</button>
|
||||||
|
<div id="modal" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: #fff; padding: 20px; border: 1px solid #ccc; z-index: 1000;">
|
||||||
|
<h3>{{ $title }}</h3>
|
||||||
|
<p>{{ $message }}</p>
|
||||||
|
<button onclick="document.getElementById('modal').style.display='none'">No</button>
|
||||||
|
<a href="{{ route('modal.cancel.confirm', ['path' => $path]) }}" style="margin-left: 10px;">
|
||||||
|
<button>Yes</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<a href="{{ $path }}">
|
||||||
|
<button>{{ $name }}</button>
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Notification Tables</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ asset('css/tables.css') }}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.table-container { margin: 24px; padding: 24px 0; }
|
||||||
|
.table-header { display: flex; justify-content: space-between; margin-bottom: 25px; }
|
||||||
|
.action-icons { color: rgb(231, 70, 16); cursor: pointer; padding: 5px 14px 5px 0; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@if (session('success'))
|
||||||
|
<div style="color: green;">{{ session('success') }}</div>
|
||||||
|
@endif
|
||||||
|
<div class="table-container">
|
||||||
|
<div class="table-header">
|
||||||
|
<div>
|
||||||
|
<input type="text" name="search" value="{{ request()->input('search') }}" placeholder="Search" onkeyup="this.form.submit()" style="width: 300px;">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button onclick="window.location.href='{{ route('notification-tables.index') }}?page=1&page_size=10'">Clear Filters</button>
|
||||||
|
<a href="{{ route('dropdown.export') }}">Export CSV</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table border="1" cellpadding="5" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
@foreach ($columns as $column)
|
||||||
|
<th>
|
||||||
|
{{ $column['title'] }}
|
||||||
|
<a href="{{ route('notification-tables.index') }}?_sort_by={{ $column['dataIndex'] }}&_sort_order={{ request()->input('_sort_by') === $column['dataIndex'] && request()->input('_sort_order') === 'asc' ? 'desc' : 'asc' }}">
|
||||||
|
{{ request()->input('_sort_by') === $column['dataIndex'] && request()->input('_sort_order') === 'asc' ? '↓' : '↑' }}
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
@endforeach
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($paginatedData as $item)
|
||||||
|
<tr>
|
||||||
|
@foreach ($columns as $column)
|
||||||
|
<td>{{ $item[$column['dataIndex']] }}</td>
|
||||||
|
@endforeach
|
||||||
|
<td>
|
||||||
|
@foreach ($actions as $action)
|
||||||
|
@if ($action['key'] === 'edit')
|
||||||
|
<a href="{{ route('edit', $item['id']) }}" class="action-icons">Edit</a>
|
||||||
|
@elseif ($action['key'] === 'delete')
|
||||||
|
<form action="{{ route('notification-tables.destroy', $item['id']) }}" method="POST" style="display: inline;">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<button type="submit" class="action-icons" onclick="return confirm('Are you sure?')">Delete</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div style="margin-top: 20px; display: flex; justify-content: space-between;">
|
||||||
|
<div>
|
||||||
|
@if ($paginatedData->total() == 0)
|
||||||
|
<p>No records found.</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $paginatedData->links() }}
|
||||||
|
<select name="page_size" onchange="this.form.submit()" style="margin-left: 10px;">
|
||||||
|
<option value="10" {{ $paginatedData->perPage() == 10 ? 'selected' : '' }}>10</option>
|
||||||
|
<option value="20" {{ $paginatedData->perPage() == 20 ? 'selected' : '' }}>20</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>404 - Page Not Found</title>
|
||||||
|
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div align="center" style="margin-top: 10%;">
|
||||||
|
<h1 style="font-size: 150px; font-weight: bold; margin: 0;">404</h1>
|
||||||
|
<p style="font-size: 30px; font-weight: bold; margin: 0;">Page not found</p>
|
||||||
|
<p>Sorry, but the page you are looking for doesn't exist</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,29 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">Users List</h1>
|
||||||
|
<form method="GET" action="{{ route('users.index') }}" class="mb-4">
|
||||||
|
<input type="text" name="search" value="{{ request()->input('search') }}" placeholder="Search..." class="border p-2 rounded" onkeyup="this.form.submit()">
|
||||||
|
</form>
|
||||||
|
<table class="w-full border-collapse border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="border p-2">
|
||||||
|
<a href="{{ route('users.index') }}?sort_field=name&sort_order={{ request()->input('sort_field') === 'name' && request()->input('sort_order') === 'asc' ? 'desc' : 'asc' }}" class="text-blue-500">
|
||||||
|
Name {{ request()->input('sort_field') === 'name' && request()->input('sort_order') === 'asc' ? '↓' : '↑' }}
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($paginatedData as $user)
|
||||||
|
<tr>
|
||||||
|
<td class="border p-2">{{ $user->name }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ $paginatedData->links() }}
|
||||||
|
</div>
|
||||||
|
@endsection
|
|
@ -1,7 +1,46 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use App\Http\Controllers\UserController;
|
||||||
|
use App\Http\Controllers\CustomTableController;
|
||||||
|
use App\Http\Controllers\Dashboard\RouteController;
|
||||||
|
use App\Http\Controllers\Dashboard\DataDisplayController;
|
||||||
|
use App\Http\Controllers\Dropdown\DropdownExportController;
|
||||||
|
use App\Http\Controllers\LoadingController;
|
||||||
|
use App\Http\Controllers\Modal\ModalCancelController;
|
||||||
|
use App\Http\Controllers\Login\LayoutController;
|
||||||
|
use App\Http\Controllers\NotificationTablesController;
|
||||||
|
use App\Http\Controllers\Page404Controller;
|
||||||
|
|
||||||
|
Route::get('/404', [Page404Controller::class, 'index'])->name('page404');
|
||||||
|
Route::get('/notification-tables', [NotificationTablesController::class, 'index'])->name('notification-tables.index');
|
||||||
|
Route::delete('/notification-tables/{id}', [NotificationTablesController::class, 'destroy'])->name('notification-tables.destroy');
|
||||||
|
Route::post('/notification-tables/batch-delete', [NotificationTablesController::class, 'batchDestroy'])->name('notification-tables.batch-delete');
|
||||||
|
|
||||||
|
Route::get('/login/layout', [LayoutController::class, 'index'])->name('login.layout');
|
||||||
|
Route::get('/modal/cancel', [ModalCancelController::class, 'index'])->name('modal.cancel');
|
||||||
|
Route::get('/modal/cancel/confirm', [ModalCancelController::class, 'confirm'])->name('modal.cancel.confirm');
|
||||||
|
|
||||||
|
Route::get('/loading', [LoadingController::class, 'index'])->name('loading');
|
||||||
|
Route::post('/dropdown/export', [DropdownExportController::class, 'export'])->name('dropdown.export');
|
||||||
|
Route::get('/dashboard/data-display', [DataDisplayController::class, 'index'])->name('dashboard.data-display');
|
||||||
|
Route::get('/view/{id}', function ($id) {
|
||||||
|
return "View page for ID: $id";
|
||||||
|
})->name('view');
|
||||||
|
|
||||||
|
Route::get('/dashboard/{component?}', [RouteController::class, 'index'])->name('dashboard.route')->where('component', '[a-zA-Z]+');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Route::get('/custom-table', [CustomTableController::class, 'index'])->name('custom-table.index');
|
||||||
|
Route::delete('/custom-table/{id}', [CustomTableController::class, 'destroy'])->name('custom-table.destroy');
|
||||||
|
Route::get('/users', [UserController::class, 'index']);
|
||||||
|
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return view('welcome');
|
return view('welcome');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Auth::routes();
|
||||||
|
|
||||||
|
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
|
||||||
|
|
|
@ -4,7 +4,10 @@ import laravel from 'laravel-vite-plugin';
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
laravel({
|
laravel({
|
||||||
input: ['resources/css/app.css', 'resources/js/app.js'],
|
input: [
|
||||||
|
'resources/sass/app.scss',
|
||||||
|
'resources/js/app.js',
|
||||||
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue