```
<?php
declare(strict_types=1);

use yii\grid\ActionColumn;
use yii\grid\GridView;
use yii\widgets\Pjax;
use yii\bootstrap5\Html;
use yii\helpers\StringHelper;
use yii\helpers\Url;

/**
* Layout Manager
* @see app\components\LayoutManager.php
* Handles need data for main.php, navbar, breadcrumbs, etc.
*/
$layout = \Yii::$app->layoutManager;//*/
?>


<?php
Pjax::begin(['id' => 'w0-pjax', 'timeout' => '5000']); ?>

<?= GridView::widget([
	'dataProvider' => $dataProvider,
	//'filterModel' => $searchModel,
	'options' => ['class' => 'table-responsive grid-view admin-grid-view'],// This class affects the container div.
	'tableOptions' => ['class' => 'table table-striped table-bordered grid-view'],// Add bootstrap table classes.
	'layout' => '{summary} {items} {pager}',
	'pager' => [
		'options' => ['class' => 'pagination mt-3 ms-2'],
		'linkOptions' => ['class' => 'page-link'],// Class for individual links.
		'prevPageLabel' => '<i class="bi bi-caret-left"></i>',// Bootstrap icon for previous page.
		'nextPageLabel' => '<i class="bi bi-caret-right"></i>',// Bootstrap icon for next page.
		//'firstPageLabel' => '<i class="bi bi-caret-left-fill"></i>',// Bootstrap icon for first page.
		//'lastPageLabel' => '<i class="bi bi-caret-right-fill"></i>',// Bootstrap icon for last page.
		'activePageCssClass' => 'active',// Class for the active page link.
		'disabledPageCssClass' => 'disabled',// Class for disabled links.
	],
	'columns' => [
		// Serial and Action Column.
		[
			'class' => 'yii\grid\CheckboxColumn',
			'name' => 'id',
			'contentOptions' => ['width' => '3%'],
        	],[
			'class' => 'yii\grid\SerialColumn'
		],[
			'class' => 'yii\grid\ActionColumn',
			'header' => 'Actions',
			'template' => '{delete} {multi-delete}',
			'buttons' => [
				'delete' => function ($url, $model, $key) use ($layout)
				{
					return ($layout->isAdmin) ? Html::a('<i class="bi bi-trash3-fill icon-size"></i>', $url, [
						'title' => \Yii::t('app', 'Delete'),
						'aria-label' => 'Delete',
						'data-confirm' => 'Are you sure you want to delete this item?',
						'data-method' => 'post',
						'data-toggle' => 'tooltip',
					]) : '';
				},
				'multi-delete' => function ($url, $model, $key) use ($layout)
				{
					return ($layout->isAdmin) ? Html::a('<i class="bi bi-eraser-fill"></i>', '#', [
						'title' => \Yii::t('app', 'Delete Selected'),
						'aria-label' => 'Delete Selected',
						'data-confirm' => 'Are you sure you want to delete this item?',
						'data-method' => 'post',
						'data-toggle' => 'tooltip',
						'data-pjax' => '1',// Allow this action from being handled by Pjax.
						'class' => 'js-batch-delete',
					]) : '';
				},
			],
			'contentOptions' => ['width' => '8.5%'],
		],
		// Serial and Action Column.
		
		/*[
			'attribute'=>'id',
			//'contentOptions'=>['width' => '10%'],
		],*/
		[
			'attribute' => 'session_id',
			'format' => 'raw',
			'value' => function ($model)
			{
				$attribute = Html::encode($model->session_id);
				$notSet = Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'text-danger']);
				
				return empty($attribute) ? $notSet : StringHelper::truncate($attribute, 16, '...');
			},
		],[
			'attribute' => 'session_token',
			'format' => 'raw',
			'value' => function ($model)
			{
				$attribute = Html::encode($model->session_token);
				$notSet = Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'text-danger']);
				
				return empty($attribute) ? $notSet : StringHelper::truncate($attribute, 16, '...');
			},
		],[
			'attribute' => 'user_ip',
			'format' => 'raw',
			'value' => function($model)
			{
				$attribute = Html::encode($model->user_ip);
				if (empty($attribute))
				{
					return Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'not-set']);
				}
				if (!filter_var($attribute, FILTER_VALIDATE_IP))
				{
					return Html::tag('span', \Yii::t('app', '(invalid IP address)'), ['class' => 'invalid-ip']);
				}
				
				return $attribute;
			},
		],[
			'attribute' => 'user_agent',
			'format' => 'raw',
			'value' => function ($model)
			{
				$attribute = Html::encode($model->user_agent);
				$notSet = Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'text-danger']);
				
				return empty($attribute) ? $notSet : StringHelper::truncate($attribute, 20, '...');
			},
		],[
			'attribute' => 'created_at',
			'format' => ['datetime', 'short'],
		],/*[
			'attribute' => 'updated_at',
			'format' => ['datetime', 'short'],
		],*/
	],
]);

Pjax::end();
?>
```

```
class SessionController extends BaseController
{
	public function behaviors(): array
	{
		$behaviors = [];
		
		$behaviors['access'] = [
			'class' => AccessControl::class,
			'only' => ['index'],
			'rules' => [
				[
					// Guest and User can't do the following:
					'actions' => ['index', 'multiple-delete', 'delete'],
					'allow' => false,
					'roles' => ['?'],
				],[
					'actions' => ['index'],
					'allow' => true,
					'roles' => ['indexUser'],
				],[
					'actions' => ['multiple-delete', 'delete'],
					'allow' => true,
					'roles' => ['deleteUser'],
				],
			],
		
		];
		
		$behaviors['verbs'] = [
			'class' => VerbFilter::class,
			'actions' => [
				'index' => ['get', 'post', 'delete'],
				'multiple-delete' => ['post', 'delete'],
				'delete' => ['post', 'delete'],
			],
		];
		
		return $behaviors;
	}
	
	public function actionIndex(): string
	{
		$dataProvider = new ActiveDataProvider([
			'query' => SessionUser::find(),
			'pagination' => [
				'pageSize' => 50
			],
			'sort' => [
				'defaultOrder' => [
					'id' => SORT_DESC,
				]
			],
		]);
		
		// Setup VanillaJS for view
		$sessionUrl = Url::toRoute(['session/multiple-delete']);
		
		$js = <<<JS
		document.addEventListener('DOMContentLoaded', function()
		{
			var batchDeleteBtn = document.querySelector('.js-batch-delete');
			if (!batchDeleteBtn)
			{
				return;
			}
			
			batchDeleteBtn.addEventListener('click', function(event)
			{
				event.preventDefault();
				
				// Get selected rows from yiiGridView
				// This example assumes selected checkboxes have 'input[name="selection[]"]:checked' inside #w0
				var checkboxes = document.querySelectorAll('#w0 input[name="selection[]"]:checked');
				var rowIds = Array.from(checkboxes).map(function(chk)
				{
					return chk.value;
				});
				
				if (rowIds.length === 0)
				{
					alert('Please select at least one item to delete.');
					
					return;
				}
				
				if (!confirm('Are you sure you want to delete the selected items?'))
				{
					return;
				}
				
				// The URL, replace with your yii Url helper output or write directly as a string
				var url = "$sessionUrl";
				
				fetch(url,
				{
					method: 'POST',
					headers: {
						'Content-Type': 'application/x-www-form-urlencoded',
						'X-Requested-With': 'XMLHttpRequest', // suggested to mimic AJAX
						'X-CSRF-Token': (document.querySelector('meta[name="csrf-token"]') || {}).content || ''
					},
					body: new URLSearchParams({row_id: rowIds})
				})
				.then(function(response)
				{
					if (!response.ok)
					{
						throw new Error('Network response was not ok');
					}
					
					return response.text();
				})
				.then(function()
				{
					// Reload the grid via PJAX equivalent
					// Pure vanilla JS PJAX reload - you have to implement reloading or trigger pjax in your environment
					var pjaxContainer = document.querySelector('#w0-pjax');
					
					if (pjaxContainer)
					{
						// If yii Pjax is used, you can trigger reload via yii Pjax js api:
						// But since this is vanilla, we'll try to reload the container via fetch
						// Or you can force full page reload:
						location.reload();
						
						// Or you can trigger a custom event or call your grid reload method accordingly
						// For demonstration, let's trigger a reload by dispatching an event:
						var event = new CustomEvent('pjax:reload');
						pjaxContainer.dispatchEvent(event);
					}
					else
					{
						// Fallback reload:
						location.reload();
					}
				})
				.catch(function()
				{
					alert('An error occurred while attempting to delete the items. Please try again.');
				});
			});
		});
		JS;
		$this->view->registerJs($js, View::POS_END);
		
		return $this->render('index', [
			'dataProvider' => $dataProvider,
		]);
	}
	
		public function actionMultipleDelete(): array
	{
		\Yii::$app->response->format = Response::FORMAT_JSON;
		
		$request = \Yii::$app->request;
		if ($request->isPost)
		{
			$data = $request->post('row_id', []);
			if (!empty($data) && is_array($data))
			{
				$transaction = \Yii::$app->db->beginTransaction();
				try
				{
					foreach ($data as $id)
					{
						$model = SessionUser::findOne($id);
						
						if ($model !== null)
						{
							$model->delete();
						}
						else
						{
							// An error occurred; roll back the transaction.
							$transaction->rollBack();
							
							return [
								'success' => false,
								'message' => 'Some items could not be found.'
							];
						}
					}
					
					// If all saves are successful, commit the transaction.
					$transaction->commit();
					
					return [
						'success' => true,
						'message' => 'Items deleted successfully.'
					];
				}
				catch (Throwable $e) {
					
					// An error occurred; roll back the transaction.
					$transaction->rollBack();
					
					return [
						'success' => false,
						'message' => 'An error occurred. Please try again.'
					];
				}
			}
			
			return [
				'success' => false,
				'message' => 'No items selected.'
			];
		}
		
		return [
			'success' => false,
			'message' => 'Invalid request method.'
		];
	}
}
```
I cant get the multi-delete to work. see anything wrong with it?