feat: rename map module to gis, refactor namespaces, and add gis RBAC role

master
Manop Kongoon 2026-02-27 13:32:39 +07:00
parent a6613fd511
commit f127c6741b
30 changed files with 1584 additions and 164 deletions

View File

@ -1,8 +1,16 @@
# Changelog
## [2026-02-27] - Modernization, Backend Tools & Dashboard Enhancements
## [2026-02-27] - Modernization, Backend Tools, GIS Refactoring & Dashboard Enhancements
### Added
- **GIS Module Refactoring:**
- Renamed legacy `map` module to `gis` for clarity and standardization.
- Performed system-wide namespace refactoring to `backend\modules\gis`.
- Redesigned GIS main dashboard with a modern Interactive Card UI and Font Awesome 5.11 icons.
- **RBAC for GIS:**
- Created a new RBAC role `gis` (GIS Data Manager) via database migration.
- Added `/gis/*` permission covering all GIS module actions.
- Configured `authManager` in console application to utilize shared RBAC files at `@backend/rbac/`.
- **Premium Backend Dashboard:**
- Redesigned the main dashboard with categorized module shortcuts for better organization.
- Categorized modules into: Administration & Strategy, Assets & Resources, and Public Services & Revenue.

View File

@ -32,6 +32,9 @@ return [
'forum' => [
'class' => 'backend\modules\forum\Module',
],
'gis' => [
'class' => 'backend\modules\gis\Module',
],
'gridview' => [
'class' => '\kartik\grid\Module'
],

24
backend/modules/gis/Module.php Executable file
View File

@ -0,0 +1,24 @@
<?php
namespace backend\modules\gis;
/**
* map module definition class
*/
class Module extends \yii\base\Module
{
/**
* {@inheritdoc}
*/
public $controllerNamespace = 'backend\modules\gis\controllers';
/**
* {@inheritdoc}
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace backend\modules\gis\controllers;
use yii\web\Controller;
/**
* Default controller for the `map` module
*/
class DefaultController extends Controller
{
/**
* Renders the index view for the module
* @return string
*/
public function actionIndex()
{
return $this->render('index');
}
}

View File

@ -0,0 +1,151 @@
<?php
namespace backend\modules\gis\controllers;
use common\models\GisGeoFeatures;
use backend\modules\gis\models\GisGeoFeaturesSearch;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* GeoFeaturesController implements the CRUD actions for GisGeoFeatures model.
*/
class GeoFeaturesController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all GisGeoFeatures models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new GisGeoFeaturesSearch();
$dataProvider = $searchModel->search($this->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single GisGeoFeatures model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new GisGeoFeatures model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new GisGeoFeatures();
if ($this->request->isPost && $model->load(Yii::$app->request->post())) {
$geo = json_decode(Yii::$app->request->post('GisGeoFeatures')['geometry'], true);
if ($geo && isset($geo['type'], $geo['coordinates'])) {
$model->geometry = new \yii\db\Expression("ST_GeomFromGeoJSON('" . json_encode($geo) . "')");
$model->type = $geo['type']; // ✅ ดึง type จาก GeoJSON โดยตรง
}
if ($model->save(false)) {
return $this->redirect(['view', 'id' => $model->id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing GisGeoFeatures model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$geo = json_decode(Yii::$app->request->post('GisGeoFeatures')['geometry'], true);
if ($geo && isset($geo['type'], $geo['coordinates'])) {
$model->geometry = new \yii\db\Expression("ST_GeomFromGeoJSON('" . json_encode($geo) . "')");
$model->type = $geo['type']; // ✅ ตั้งค่า type เช่นกัน
}
if ($model->save(false)) {
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('update', [
'model' => $model,
]);
}
/**
* Deletes an existing GisGeoFeatures model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the GisGeoFeatures model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param int $id ID
* @return GisGeoFeatures the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = GisGeoFeatures::findOne(['id' => $id])) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

View File

@ -0,0 +1,134 @@
<?php
namespace backend\modules\gis\controllers;
use common\models\GisBase;
use backend\modules\gis\models\GisBaseSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* GisBaseController implements the CRUD actions for GisBase model.
*/
class GisBaseController extends Controller
{
/**
* @inheritDoc
*/
public function behaviors()
{
return array_merge(
parent::behaviors(),
[
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['POST'],
],
],
]
);
}
/**
* Lists all GisBase models.
*
* @return string
*/
public function actionIndex()
{
$searchModel = new GisBaseSearch();
$dataProvider = $searchModel->search($this->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single GisBase model.
* @param int $id ID
* @return string
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new GisBase model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return string|\yii\web\Response
*/
public function actionCreate()
{
$model = new GisBase();
if ($this->request->isPost) {
if ($model->load($this->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
/**
* Updates an existing GisBase model.
* If update is successful, the browser will be redirected to the 'view' page.
* @param int $id ID
* @return string|\yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($this->request->isPost && $model->load($this->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('update', [
'model' => $model,
]);
}
/**
* Deletes an existing GisBase model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* @param int $id ID
* @return \yii\web\Response
* @throws NotFoundHttpException if the model cannot be found
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the GisBase model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* @param int $id ID
* @return GisBase the loaded model
* @throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = GisBase::findOne(['id' => $id])) !== null) {
return $model;
}
throw new NotFoundHttpException('The requested page does not exist.');
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace backend\modules\gis\controllers;
use common\models\GisGeoFeatures;
use Yii;
use yii\db\Query;
use yii\web\Response;
class MapController extends \yii\web\Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionMap()
{
return $this->render('map');
}
public function actionGeojson()
{
Yii::$app->response->format = Response::FORMAT_JSON;
$gis_base_id = Yii::$app->request->get('GisGeoFeatures')['gis_base_id'] ?? null;
$features = [];
// ✅ โหลดข้อมูลจากตาราง gis_tambon เสมอ
$tambonRows = (new Query())
->select(['id', 'tb_name_t', 'tb_code', 'ST_AsGeoJSON(geom) AS geojson'])
->from('gis_tambon')
->where(['id' => 7662])
->all();
foreach ($tambonRows as $row) {
$features[] = [
'type' => 'Feature',
'geometry' => json_decode($row['geojson']),
'properties' => [
'id' => $row['id'],
'name' => $row['tb_name_t'],
'interactive' => false
]
];
}
// ✅ โหลดข้อมูลจาก gis_geo_features ตาม gis_base_id (ถ้ามี)
$geoQuery = (new Query())
->select([
'f.id',
'f.name',
'ST_AsGeoJSON(f.geometry) AS geojson',
'b.color' // 👈 ดึงสีจากตาราง gis_base
])
->from(['f' => 'gis_geo_features'])
->leftJoin(['b' => 'gis_base'], 'f.gis_base_id = b.id'); // 👈 join
if ($gis_base_id) {
$geoQuery->where(['f.gis_base_id' => $gis_base_id]);
}
$geoRows = $geoQuery->all();
foreach ($geoRows as $row) {
$features[] = [
'type' => 'Feature',
'geometry' => json_decode($row['geojson']),
'properties' => [
'id' => $row['id'],
'name' => $row['name'],
'color' => $row['color'] ?? '#3388ff' // ✅ กำหนดสี default ถ้าไม่มี
]
];
}
return [
'type' => 'FeatureCollection',
'features' => $features
];
}
/*public function actionGeojson()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$gis_base_id = Yii::$app->request->get('GisGeoFeatures')['gis_base_id'] ?? null;
$query = GisGeoFeatures::find();
if ($gis_base_id) {
$query->andWhere(['gis_base_id' => $gis_base_id]);
}
$features = $query->all();
$geojson = [
"type" => "FeatureCollection",
"features" => []
];
foreach ($features as $f) {
$geojson['features'][] = [
"type" => "Feature",
"geometry" => json_decode($f->geometry),
"properties" => [
"name" => $f->name
]
];
}
return $geojson;
}*/
}

View File

@ -0,0 +1,74 @@
<?php
namespace backend\modules\gis\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\GisBase;
/**
* GisBaseSearch represents the model behind the search form of `common\models\GisBase`.
*/
class GisBaseSearch extends GisBase
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id', 'created_at', 'updated_at', 'created_by', 'updated_by'], 'integer'],
[['name', 'description'], 'safe'],
];
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
* @param string|null $formName Form name to be used into `->load()` method.
*
* @return ActiveDataProvider
*/
public function search($params, $formName = null)
{
$query = GisBase::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params, $formName);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_by' => $this->created_by,
'updated_by' => $this->updated_by,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'description', $this->description]);
return $dataProvider;
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace backend\modules\gis\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use common\models\GisGeoFeatures;
/**
* GisGeoFeaturesSearch represents the model behind the search form of `common\models\GisGeoFeatures`.
*/
class GisGeoFeaturesSearch extends GisGeoFeatures
{
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['id', 'gis_base_id', 'created_at', 'updated_at', 'created_by', 'updated_by'], 'integer'],
[['name', 'geometry', 'type'], 'safe'],
];
}
/**
* {@inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
* @param string|null $formName Form name to be used into `->load()` method.
*
* @return ActiveDataProvider
*/
public function search($params, $formName = null)
{
$query = GisGeoFeatures::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params, $formName);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'gis_base_id' => $this->gis_base_id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'created_by' => $this->created_by,
'updated_by' => $this->updated_by,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'geometry', $this->geometry])
->andFilterWhere(['like', 'type', $this->type]);
return $dataProvider;
}
}

View File

@ -0,0 +1,109 @@
<?php
use yii\helpers\Html;
use yii\helpers\Url;
$this->title = 'ระบบภูมิสารสนเทศ (GIS)';
$this->params['breadcrumbs'][] = $this->title;
?>
<style>
.gis-card {
border: none;
border-radius: 20px;
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
background: #fff;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 25px;
text-align: center;
text-decoration: none !important;
color: #2c1810;
border: 1.5px solid transparent;
}
.gis-card:hover {
transform: translateY(-8px);
box-shadow: 0 15px 35px rgba(255, 192, 104, 0.3);
border-color: #FFC068;
}
.gis-icon-wrap {
width: 90px;
height: 90px;
background: #FFDBBB;
border-radius: 22px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 25px;
font-size: 2.8rem;
color: #d35400;
transition: all 0.3s;
}
.gis-card:hover .gis-icon-wrap {
background: #FFC068;
color: #8e1d1d;
transform: rotate(8deg) scale(1.1);
}
.gis-label {
font-weight: 800;
font-size: 1.25rem;
margin-bottom: 12px;
color: #2c1810;
}
.gis-desc {
font-size: 0.9rem;
color: #777;
line-height: 1.5;
}
.category-header {
background: linear-gradient(135deg, #2c1810 0%, #8e1d1d 100%);
color: white;
padding: 30px;
border-radius: 20px;
margin-bottom: 40px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
</style>
<div class="gis-default-index">
<div class="category-header">
<h2 class="font-weight-bold mb-1"><i class="fas fa-map-marked-alt mr-2"></i> ศูนย์จัดการข้อมูลภูมิสารสนเทศ</h2>
<p class="mb-0 opacity-75">จัดการข้อมูลตำแหน่ง พิกัด และแผนที่ยุทธศาสตร์เพื่อการพัฒนาท้องถิ่น</p>
</div>
<div class="row">
<!-- 1. Map Overview -->
<div class="col-md-4 mb-4">
<a href="<?= Url::to(['/gis/map/index']) ?>" class="gis-card">
<div class="gis-icon-wrap"><i class="fas fa-map"></i></div>
<div class="gis-label">ภาพรวมแผนที่</div>
<div class="gis-desc">แสดงตำแหน่งพิกัดสำคัญทั้งหมดบนแผนที่ฐานแบบโต้ตอบ (Interactive Map)</div>
</a>
</div>
<!-- 2. Geo Features -->
<div class="col-md-4 mb-4">
<a href="<?= Url::to(['/gis/geo-features/index']) ?>" class="gis-card">
<div class="gis-icon-wrap"><i class="fas fa-map-marker-alt"></i></div>
<div class="gis-label">จัดการจุดพิกัด (POI)</div>
<div class="gis-desc">บันทึกตำแหน่งสถานที่สำคัญ เช่น โรงเรียน วัด แหล่งน้ำ และจุดเสี่ยงภัย</div>
</a>
</div>
<!-- 3. Base Data -->
<div class="col-md-4 mb-4">
<a href="<?= Url::to(['/gis/gis-base/index']) ?>" class="gis-card">
<div class="gis-icon-wrap"><i class="fas fa-database"></i></div>
<div class="gis-label">ข้อมูลพื้นฐาน GIS</div>
<div class="gis-desc">จัดการขอบเขตการปกครอง เส้นทางคมนาคม และข้อมูลเชิงพื้นที่พื้นฐาน</div>
</a>
</div>
</div>
<div class="alert alert-info border-0 rounded-pill mt-4 shadow-sm px-4">
<i class="fas fa-info-circle mr-2"></i> <strong>คำแนะนำ:</strong> คุณสามารถดึงข้อมูลพิกัดจากระบบ GIS นี้ไปแสดงผลที่หน้าเว็บไซต์ (Frontend) เพื่ออำนวยความสะดวกแก่ประชาชนได้ทันที
</div>
</div>

View File

@ -0,0 +1,95 @@
<?php
use common\models\GisBase;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var common\models\GisGeoFeatures $model */
/** @var yii\widgets\ActiveForm $form */
?>
<!-- Leaflet -->
<?php $this->registerCssFile("https://unpkg.com/leaflet/dist/leaflet.css") ?>
<?php $this->registerJsFile("https://unpkg.com/leaflet/dist/leaflet.js") ?>
<!-- Leaflet Draw -->
<?php $this->registerCssFile("https://unpkg.com/leaflet-draw/dist/leaflet.draw.css") ?>
<?php $this->registerJsFile("https://unpkg.com/leaflet-draw/dist/leaflet.draw.js") ?>
<div id="map" style="height: 600px;"></div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const map = L.map("map").setView([15.233509, 104.325743], 14); // ตำแหน่งเริ่มต้นประเทศไทย
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors"
}).addTo(map);
const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
draw: {
marker: true, // จุด (Point)
polyline: true, // เส้น (Line)
polygon: true, // พื้นที่ (Polygon)
circle: false,
rectangle: false
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
// เก็บ Geometry ลง hidden input
map.on(L.Draw.Event.CREATED, function(e) {
const layer = e.layer;
drawnItems.addLayer(layer);
const geojson = layer.toGeoJSON();
document.getElementById("geometry").value = JSON.stringify(geojson.geometry);
});
// ลบแล้วล้างค่า geometry
map.on(L.Draw.Event.DELETED, function() {
document.getElementById("geometry").value = "";
});
fetch('<?= Url::to(['/map/map/geojson']) ?>') // ✅ URL ไปยัง controller ที่คุณสร้าง
.then(res => res.json())
.then(data => {
L.geoJSON(data, {
onEachFeature: function(feature, layer) {
layer.bindPopup('' + feature.properties.name);
},
style: {
color: '#3388ff',
weight: 2,
fillOpacity: 0.2
}
}).addTo(map);
});
});
</script>
<div class="gis-geo-features-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'gis_base_id')->dropDownList(ArrayHelper::map(GisBase::find()->all(), 'id', 'name')) ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'geometry')->label(false)->hiddenInput(['id' => 'geometry']) ?>
<div class="form-group">
<?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -0,0 +1,43 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var backend\modules\gis\models\GisGeoFeaturesSearch $model */
/** @var yii\widgets\ActiveForm $form */
?>
<div class="gis-geo-features-search">
<?php $form = ActiveForm::begin([
'action' => ['index'],
'method' => 'get',
]); ?>
<?= $form->field($model, 'id') ?>
<?= $form->field($model, 'gis_base_id') ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'geometry') ?>
<?= $form->field($model, 'type') ?>
<?php // echo $form->field($model, 'created_at') ?>
<?php // echo $form->field($model, 'updated_at') ?>
<?php // echo $form->field($model, 'created_by') ?>
<?php // echo $form->field($model, 'updated_by') ?>
<div class="form-group">
<?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
<?= Html::resetButton('Reset', ['class' => 'btn btn-outline-secondary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -0,0 +1,20 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var common\models\GisGeoFeatures $model */
$this->title = 'เพิ่มรายการ';
$this->params['breadcrumbs'][] = ['label' => 'รายการ', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="gis-geo-features-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

View File

@ -0,0 +1,56 @@
<?php
use common\models\GisGeoFeatures;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\grid\ActionColumn;
use yii\grid\GridView;
/** @var yii\web\View $this */
/** @var backend\modules\gis\models\GisGeoFeaturesSearch $searchModel */
/** @var yii\data\ActiveDataProvider $dataProvider */
$this->title = 'ข้อมูลพิกัด';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="gis-geo-features-index">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a('เพิ่มข้อมูล', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
[
'attribute' => 'gis_base_id',
'value' => function($model) {
return $model->gisBase->name;
}
],
'name',
//'geometry',
//'type',
//'created_at',
//'updated_at',
//'created_by',
//'updated_by',
[
'class' => ActionColumn::className(),
'urlCreator' => function ($action, GisGeoFeatures $model, $key, $index, $column) {
return Url::toRoute([$action, 'id' => $model->id]);
}
],
],
]); ?>
</div>

View File

@ -0,0 +1,21 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var common\models\GisGeoFeatures $model */
$this->title = 'Update Gis Geo Features: ' . $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Gis Geo Features', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="gis-geo-features-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

View File

@ -0,0 +1,44 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/** @var yii\web\View $this */
/** @var common\models\GisGeoFeatures $model */
$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Gis Geo Features', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
\yii\web\YiiAsset::register($this);
?>
<div class="gis-geo-features-view">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a('Delete', ['delete', 'id' => $model->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
]) ?>
</p>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'gis_base_id',
'name',
'geometry',
'type',
'created_at',
'updated_at',
'created_by',
'updated_by',
],
]) ?>
</div>

View File

@ -0,0 +1,29 @@
<?php
use kartik\color\ColorInput;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var common\models\GisBase $model */
/** @var yii\widgets\ActiveForm $form */
?>
<div class="gis-base-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'color')->widget(ColorInput::classname(), [
'options' => ['placeholder' => 'Select color ...'],
])?>
<?= $form->field($model, 'description')->textarea(['rows' => 6]) ?>
<div class="form-group">
<?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -0,0 +1,39 @@
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/** @var yii\web\View $this */
/** @var backend\modules\gis\models\GisBaseSearch $model */
/** @var yii\widgets\ActiveForm $form */
?>
<div class="gis-base-search">
<?php $form = ActiveForm::begin([
'action' => ['index'],
'method' => 'get',
]); ?>
<?= $form->field($model, 'id') ?>
<?= $form->field($model, 'name') ?>
<?= $form->field($model, 'description') ?>
<?= $form->field($model, 'created_at') ?>
<?= $form->field($model, 'updated_at') ?>
<?php // echo $form->field($model, 'created_by') ?>
<?php // echo $form->field($model, 'updated_by') ?>
<div class="form-group">
<?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
<?= Html::resetButton('Reset', ['class' => 'btn btn-outline-secondary']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>

View File

@ -0,0 +1,20 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var common\models\GisBase $model */
$this->title = 'เพิ่มชั้นข้อมูล';
$this->params['breadcrumbs'][] = ['label' => 'ชั้นข้อมูล', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="gis-base-create">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

View File

@ -0,0 +1,49 @@
<?php
use common\models\GisBase;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\grid\ActionColumn;
use yii\grid\GridView;
/** @var yii\web\View $this */
/** @var backend\modules\gis\models\GisBaseSearch $searchModel */
/** @var yii\data\ActiveDataProvider $dataProvider */
$this->title = 'ชั้นข้อมูล';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="gis-base-index">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a('เพิ่มข้อมูล', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'name',
'description:ntext',
//'created_at',
//'updated_at',
//'created_by',
//'updated_by',
[
'class' => ActionColumn::className(),
'urlCreator' => function ($action, GisBase $model, $key, $index, $column) {
return Url::toRoute([$action, 'id' => $model->id]);
}
],
],
]); ?>
</div>

View File

@ -0,0 +1,21 @@
<?php
use yii\helpers\Html;
/** @var yii\web\View $this */
/** @var common\models\GisBase $model */
$this->title = 'แก้ไข: ' . $model->name;
$this->params['breadcrumbs'][] = ['label' => 'ชั้นข้อมูล', 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'แก้ไข';
?>
<div class="gis-base-update">
<h1><?= Html::encode($this->title) ?></h1>
<?= $this->render('_form', [
'model' => $model,
]) ?>
</div>

View File

@ -0,0 +1,42 @@
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
/** @var yii\web\View $this */
/** @var common\models\GisBase $model */
$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Gis Bases', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
\yii\web\YiiAsset::register($this);
?>
<div class="gis-base-view">
<h1><?= Html::encode($this->title) ?></h1>
<p>
<?= Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?>
<?= Html::a('Delete', ['delete', 'id' => $model->id], [
'class' => 'btn btn-danger',
'data' => [
'confirm' => 'Are you sure you want to delete this item?',
'method' => 'post',
],
]) ?>
</p>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'name',
'description:ntext',
'created_at',
'updated_at',
'created_by',
'updated_by',
],
]) ?>
</div>

View File

@ -0,0 +1,9 @@
<?php
/** @var yii\web\View $this */
?>
<h1>map/index</h1>
<p>
You may change the content of this page by modifying
the file <code><?= __FILE__; ?></code>.
</p>

View File

@ -0,0 +1,134 @@
<?php
use common\models\GisBase;
use yii\helpers\Url;
$this->title = 'แผนที่';
?>
<!-- Leaflet -->
<?php $this->registerCssFile("https://unpkg.com/leaflet/dist/leaflet.css") ?>
<?php $this->registerJsFile("https://unpkg.com/leaflet/dist/leaflet.js") ?>
<!-- Leaflet Draw -->
<?php $this->registerCssFile("https://unpkg.com/leaflet-draw/dist/leaflet.draw.css") ?>
<?php $this->registerJsFile("https://unpkg.com/leaflet-draw/dist/leaflet.draw.js") ?>
<?php $this->registerCssFile("https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css") ?>
<h5><i class="bi bi-layers"></i> เลือกชั้นข้อมูล</h5>
<select id="layerSelector" class="form-select form-select-sm mt-2">
<option value="">-- กรุณาเลือก --</option>
<?php foreach (GisBase::find()->all() as $base) { ?>
<option value="<?= $base->id ?>"><?= $base->name ?></option>
<?php } ?>
</select>
<div id="map" style="height: 800px;"></div>
<style>
#map {
height: 800px;
border-radius: 0.5rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 1px solid #ccc;
}
.map-wrapper {
padding: 1rem;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const map = L.map("map").setView([15.233509, 104.325743], 14);
const geoLayerGroup = L.layerGroup().addTo(map);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors"
}).addTo(map);
const layerSelector = document.getElementById("layerSelector");
function loadGeoLayer(gis_base_id = "") {
geoLayerGroup.clearLayers();
let url = '<?= Url::to(['/map/map/geojson']) ?>';
if (gis_base_id) {
url += '?GisGeoFeatures[gis_base_id]=' + encodeURIComponent(gis_base_id);
}
fetch(url)
.then(res => res.json())
.then(data => {
L.geoJSON(data, {
/*pointToLayer: function(feature, latlng) {
const color = feature.properties?.color || '#dc3545';
return L.circleMarker(latlng, {
radius: 8,
fillColor: color,
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
},*/
pointToLayer: function(feature, latlng) {
const color = feature.properties?.color || '#007bff';
const icon = L.divIcon({
className: 'custom-div-icon',
html: `<div style="background:${color}; width:12px; height:12px; border-radius:50%; border:2px solid white;"></div>`,
iconSize: [16, 16],
iconAnchor: [8, 8]
});
return L.marker(latlng, {
icon
});
},
style: function(feature) {
return {
color: feature.properties?.color || '#0d6efd',
weight: 2,
fillOpacity: 0.3
};
},
onEachFeature: function(feature, layer) {
if (feature.properties.interactive === false) {
// ✅ ปิดการคลิกเฉพาะ feature นี้
layer.options.interactive = false;
layer.off(); // remove all event listeners
} else {
const name = feature.properties?.name || 'ไม่มีชื่อ';
layer.bindPopup(`<b>${name}</b>`);
}
}
}).addTo(geoLayerGroup);
});
}
// โหลดทั้งหมดตอนเริ่ม
loadGeoLayer();
// เมื่อเปลี่ยน dropdown
layerSelector.addEventListener('change', function() {
const selected = this.value;
loadGeoLayer(selected);
});
});
</script>
<?php if(false){?>
<div class="card">
<div class="card-header">
<h4 class="card-title">รายการข้อมูล</h4>
</div>
<div class="card-body">
</div>
</div>
<?php }?>

View File

@ -1,4 +1,5 @@
<?php
return [
'/administrator/*' => [
'type' => 2,
@ -51,4 +52,15 @@ return [
'pms_forum',
],
],
'/gis/*' => [
'type' => 2,
'description' => 'Access to all GIS module actions',
],
'gis' => [
'type' => 1,
'description' => 'GIS Data Manager',
'children' => [
'/gis/*',
],
],
];

View File

@ -59,21 +59,13 @@ if (Yii::$app->user->can('cms')) {
]];
$menu[] = ['label' => '<i class="fa fa-list"></i> <span>เมนู</span>', 'url' => ['/cms/menu/index'], 'items' => [
/*$menu[] = ['label' => '<i class="fa fa-list"></i> <span>เมนู</span>', 'url' => ['/cms/menu/index'], 'items' => [
['label' => '<span>กลุ่มเมนู</span>', 'url' => ['/cms/menu-group/index']],
['label' => '<span>เมนู</span>', 'url' => ['/cms/menu/index']],
]];
/*$menu[] = ['label' => '<i class="fa fa-users"></i> <span>คนเก่งคนดี</span>', 'url' => ['/cms/great-person/index'], 'items' => [
['label' => '<span>รายการคนเก่งคนดี</span>', 'url' => ['/cms/great-person/index']],
['label' => '<span>เพิ่มคนเก่งคนดี</span>', 'url' => ['/cms/great-person/create']],
]];
$menu[] = ['label' => '<i class="fa fa-sticky-note"></i> <span>LINE Notify</span>', 'url' => ['/cms/line-notify/index'], 'items' => [
['label' => '<span>รายการแจ้งเตือน</span>', 'url' => ['/cms/line-notify/index']],
['label' => '<span>เพิ่มรายการแจ้งเตือน</span>', 'url' => ['/cms/line-notify/create']],
]];*/
}
//Administrator
//Forum
if (Yii::$app->user->can('forum')) {
$menu[] = ['label' => '<span>ระบบกระดานข่าว</span>', 'options' => ['class' => 'navigation-header']];
$menu[] = ['label' => '<i class="fa fa-comments"></i> <span>กระทู้</span>', 'url' => ['/forum/thread/index'], 'items' => [
@ -82,19 +74,29 @@ if (Yii::$app->user->can('forum')) {
['label' => '<span>รายการคอมเม้น</span>', 'url' => ['/forum/comment/index']],
]];
}//user can administrator
}//user can forum
//GIS
if (Yii::$app->user->can('gis')) {
$menu[] = ['label' => '<span>ระบบภูมิสารสนเทศ (GIS)</span>', 'options' => ['class' => 'navigation-header']];
$menu[] = ['label' => '<i class="fa fa-map-marker"></i> <span>ระบบ GIS</span>', 'url' => ['/gis/default/index'], 'items' => [
['label' => '<span>ภาพรวมแผนที่</span>', 'url' => ['/gis/map/index']],
['label' => '<span>จัดการจุดพิกัด</span>', 'url' => ['/gis/geo-features/index']],
['label' => '<span>ข้อมูลพื้นฐาน GIS</span>', 'url' => ['/gis/gis-base/index']],
]];
}
//Administrator
if (Yii::$app->user->can('administrator')) {
$menu[] = ['label' => '<span>ผู้ดูแลระบบ</span>', 'options' => ['class' => 'navigation-header']];
$menu[] = ['label' => '<i class="fa fa-users"></i> <span>ผู้ใช้งาน</span>', 'url' => ['/administrator/user/index'], 'items' => [
['label' => '<span>รายการผู้ใช้งาน</span>', 'url' => ['/administrator/user/index']],
['label' => '<span>เพิ่มผู้ใช้งาน</span>', 'url' => ['/administrator/user/create']],
//['label' => '<span>เมนู</span>', 'url' => ['/administrator/menu/index']],
]];
$menu[] = ['label' => '<i class="fa fa-list"></i> <span>เมนูหน้าบ้าน</span>', 'url' => ['/administrator/menu-frontend/index']];
$menu[] = ['label' => '<i class="fa fa-paint-brush"></i> <span>จัดการไฟล์ธีม</span>', 'url' => ['/administrator/theme-editor/index']];
$menu[] = ['label' => '<i class="fa fa-folder-open"></i> <span>จัดการไฟล์อัปโหลด</span>', 'url' => ['/administrator/filemanager/index']];
$menu[] = ['label' => '<i class="fa fa-users"></i> <span>ผู้ใช้งาน</span>', 'url' => ['/administrator/user/index'], 'items' => [
['label' => '<span>รายการผู้ใช้งาน</span>', 'url' => ['/administrator/user/index']],
['label' => '<span>เพิ่มผู้ใช้งาน</span>', 'url' => ['/administrator/user/create']],
]];
$menu[] = ['label' => '<i class="fa fa-lock"></i> <span>ควบคุมสิทธิ์การเข้าถึง</span>', 'url' => ['/admin/assignment/index'], 'items' => [
['label' => '<span>การกำหนด</span>', 'url' => ['/admin/assignment/index']],
['label' => '<span>บทบาท</span>', 'url' => ['/admin/role/index']],

View File

@ -0,0 +1,149 @@
<?php
use yii\helpers\Url;
?>
<!-- SECTION 1: Administration & Strategy -->
<div class="category-title mt-2">
<i class="fas fa-briefcase"></i> 1. งานบริหารและยุทธศาสตร์
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/saraban/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-envelope-open-text"></i></div>
<span class="module-label">งานสารบรรณ</span>
<span class="module-sub-label">E-Saraban</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/personnel/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-user-tie"></i></div>
<span class="module-label">งานบุคลากร</span>
<span class="module-sub-label">Personnel</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/personnel/checkin/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-user-clock"></i></div>
<span class="module-label">ลงเวลาทำงาน</span>
<span class="module-sub-label">Attendance</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/travel/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-route"></i></div>
<span class="module-label">ไปราชการ</span>
<span class="module-sub-label">Travel</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/plan/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-clipboard-list"></i></div>
<span class="module-label">งานแผน</span>
<span class="module-sub-label">Planning</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/ita/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-balance-scale"></i></div>
<span class="module-label">งานโปร่งใส ITA</span>
<span class="module-sub-label">Transparency</span>
</a>
</div>
</div>
<!-- SECTION 2: Assets & Resources -->
<div class="category-title">
<i class="fas fa-boxes"></i> 2. งานพัสดุและทรัพยากร
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/supply/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-cubes"></i></div>
<span class="module-label">งานครุภัณฑ์</span>
<span class="module-sub-label">Supply</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/durable/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-tools"></i></div>
<span class="module-label">งานวัสดุ</span>
<span class="module-sub-label">Durable</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/fixed/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-wrench"></i></div>
<span class="module-label">แจ้งซ่อม</span>
<span class="module-sub-label">Maintenance</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/car/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-car"></i></div>
<span class="module-label">จองรถ</span>
<span class="module-sub-label">Vehicles</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/room/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-handshake"></i></div>
<span class="module-label">จองห้องประชุม</span>
<span class="module-sub-label">Rooms</span>
</a>
</div>
</div>
<!-- SECTION 3: Public Services & Revenue -->
<div class="category-title">
<i class="fas fa-hand-holding-heart"></i> 3. งานบริการสาธารณะและรายได้
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/tax/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-file-invoice-dollar"></i></div>
<span class="module-label">งานรายได้/ภาษี</span>
<span class="module-sub-label">Taxation</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/waterworks/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-tint"></i></div>
<span class="module-label">งานประปา</span>
<span class="module-sub-label">Waterworks</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/welfare/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-heartbeat"></i></div>
<span class="module-label">สวัสดิการสังคม</span>
<span class="module-sub-label">Welfare</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/garbage/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-recycle"></i></div>
<span class="module-label">จัดการขยะ</span>
<span class="module-sub-label">Waste Mgmt</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/ems/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-ambulance"></i></div>
<span class="module-label">EMS 1669</span>
<span class="module-sub-label">Emergency</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/disaster/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-fire-extinguisher"></i></div>
<span class="module-label">งานป้องกันฯ</span>
<span class="module-sub-label">Disaster</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/gis/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-map-marked-alt"></i></div>
<span class="module-label">GIS Map</span>
<span class="module-sub-label">Strategic</span>
</a>
</div>
</div>

View File

@ -186,152 +186,7 @@ $recentPosts = CmsPost::find()->orderBy(['created_at' => SORT_DESC])->limit(5)->
</div>
</div>
<!-- SECTION 1: Administration & Strategy -->
<div class="category-title mt-2">
<i class="fas fa-briefcase"></i> 1. งานบริหารและยุทธศาสตร์
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/saraban/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-envelope-open-text"></i></div>
<span class="module-label">งานสารบรรณ</span>
<span class="module-sub-label">E-Saraban</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/personnel/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-user-tie"></i></div>
<span class="module-label">งานบุคลากร</span>
<span class="module-sub-label">Personnel</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/personnel/checkin/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-user-clock"></i></div>
<span class="module-label">ลงเวลาทำงาน</span>
<span class="module-sub-label">Attendance</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/travel/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-route"></i></div>
<span class="module-label">ไปราชการ</span>
<span class="module-sub-label">Travel</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/plan/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-clipboard-list"></i></div>
<span class="module-label">งานแผน</span>
<span class="module-sub-label">Planning</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/ita/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-balance-scale"></i></div>
<span class="module-label">งานโปร่งใส ITA</span>
<span class="module-sub-label">Transparency</span>
</a>
</div>
</div>
<!-- SECTION 2: Assets & Resources -->
<div class="category-title">
<i class="fas fa-boxes"></i> 2. งานพัสดุและทรัพยากร
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/supply/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-cubes"></i></div>
<span class="module-label">งานครุภัณฑ์</span>
<span class="module-sub-label">Supply</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/durable/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-tools"></i></div>
<span class="module-label">งานวัสดุ</span>
<span class="module-sub-label">Durable</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/fixed/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-wrench"></i></div>
<span class="module-label">แจ้งซ่อม</span>
<span class="module-sub-label">Maintenance</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/car/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-car"></i></div>
<span class="module-label">จองรถ</span>
<span class="module-sub-label">Vehicles</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/room/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-handshake"></i></div>
<span class="module-label">จองห้องประชุม</span>
<span class="module-sub-label">Rooms</span>
</a>
</div>
</div>
<!-- SECTION 3: Public Services & Revenue -->
<div class="category-title">
<i class="fas fa-hand-holding-heart"></i> 3. งานบริการสาธารณะและรายได้
</div>
<div class="row mb-4">
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/tax/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-file-invoice-dollar"></i></div>
<span class="module-label">งานรายได้/ภาษี</span>
<span class="module-sub-label">Taxation</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/waterworks/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-tint"></i></div>
<span class="module-label">งานประปา</span>
<span class="module-sub-label">Waterworks</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/welfare/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-heartbeat"></i></div>
<span class="module-label">สวัสดิการสังคม</span>
<span class="module-sub-label">Welfare</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/garbage/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-recycle"></i></div>
<span class="module-label">จัดการขยะ</span>
<span class="module-sub-label">Waste Mgmt</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/ems/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-ambulance"></i></div>
<span class="module-label">EMS 1669</span>
<span class="module-sub-label">Emergency</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/disaster/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-fire-extinguisher"></i></div>
<span class="module-label">งานป้องกันฯ</span>
<span class="module-sub-label">Disaster</span>
</a>
</div>
<div class="col-6 col-md-4 col-lg-3 col-xl-2 mb-3">
<a href="<?= Url::to(['/gis/default/index']) ?>" class="module-card">
<div class="module-icon-wrap"><i class="fas fa-map-marked-alt"></i></div>
<span class="module-label">GIS Map</span>
<span class="module-sub-label">Strategic</span>
</a>
</div>
</div>
<?php //= $this->render('_menu') ?>
<div class="row">
<!-- Left Column: Visitor Details & Recent Posts -->
@ -450,7 +305,7 @@ $recentPosts = CmsPost::find()->orderBy(['created_at' => SORT_DESC])->limit(5)->
<div class="card-body text-center py-4">
<i class="fas fa-info-circle mb-3" style="font-size: 2.5rem; opacity: 0.5;"></i>
<h5>ระบบจัดการเว็บไซต์</h5>
<p class="small opacity-75">เวอร์ชัน 2.0.0 (Yii2 Core)<br>พัฒนาโดย HANUMANIT Co., Ltd.</p>
<p class="small opacity-75">เวอร์ชัน 2.0.0<br>พัฒนาโดย HANUMANIT Co., Ltd.</p>
<hr style="border-color: rgba(255,255,255,0.1);">
<div class="d-flex justify-content-around small">
<div><i class="fas fa-check-circle mr-1"></i> SSL Protected</div>

View File

@ -30,6 +30,12 @@ return [
],
],
],
'authManager' => [
'class' => 'yii\rbac\PhpManager',
'itemFile' => '@backend/rbac/items.php',
'assignmentFile' => '@backend/rbac/assignments.php',
'ruleFile' => '@backend/rbac/rules.php',
],
],
'params' => $params,
];

View File

@ -0,0 +1,60 @@
<?php
use yii\db\Migration;
/**
* Class m260227_061319_add_gis_rbac_role
*/
class m260227_061319_add_gis_rbac_role extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$auth = Yii::$app->authManager;
// 1. Create the permission for the entire GIS module
$gisPermission = $auth->getPermission('/gis/*');
if (!$gisPermission) {
$gisPermission = $auth->createPermission('/gis/*');
$gisPermission->description = 'Access to all GIS module actions';
$auth->add($gisPermission);
}
// 2. Create the 'gis' role
$gisRole = $auth->getRole('gis');
if (!$gisRole) {
$gisRole = $auth->createRole('gis');
$gisRole->description = 'GIS Data Manager';
$auth->add($gisRole);
}
// 3. Assign permission to the role
if (!$auth->hasChild($gisRole, $gisPermission)) {
$auth->addChild($gisRole, $gisPermission);
}
echo "RBAC: Role 'gis' and permission '/gis/*' have been created successfully.\n";
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$auth = Yii::$app->authManager;
$gisRole = $auth->getRole('gis');
if ($gisRole) {
$auth->remove($gisRole);
}
$gisPermission = $auth->getPermission('/gis/*');
if ($gisPermission) {
$auth->remove($gisPermission);
}
return true;
}
}