<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>แดชบอร์ดแนวโน้มการเมืองไทย - PHP Version</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<style>
/* สไตล์พื้นฐาน */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
color: white;
padding: 30px;
text-align: center;
position: relative;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
position: relative;
z-index: 1;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
position: relative;
z-index: 1;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
padding: 30px;
background: #f8f9fa;
}
.stat-card {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
text-align: center;
transition: transform 0.3s ease, box-shadow 0.3s ease;
border-left: 4px solid #3498db;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.stat-card h3 {
color: #2c3e50;
font-size: 1.8rem;
margin-bottom: 10px;
}
.stat-card p {
color: #7f8c8d;
font-size: 0.9rem;
}
.charts-section {
padding: 30px;
}
.chart-container {
background: white;
margin-bottom: 30px;
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
.chart-title {
font-size: 1.5rem;
color: #2c3e50;
margin-bottom: 20px;
text-align: center;
padding-bottom: 10px;
border-bottom: 2px solid #ecf0f1;
}
.chart-wrapper {
position: relative;
height: 400px;
margin-bottom: 20px;
}
.data-source {
text-align: center;
color: #7f8c8d;
font-size: 0.85rem;
font-style: italic;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #ecf0f1;
}
.filter-section {
background: #34495e;
padding: 20px 30px;
color: white;
}
.filter-controls {
display: flex;
gap: 20px;
align-items: center;
flex-wrap: wrap;
}
.filter-group {
display: flex;
align-items: center;
gap: 10px;
}
.filter-group label {
font-weight: 500;
}
.filter-group select {
padding: 8px 12px;
border: none;
border-radius: 8px;
background: white;
color: #2c3e50;
font-size: 0.9rem;
}
.update-info {
background: #2ecc71;
color: white;
padding: 15px 30px;
text-align: center;
font-size: 0.9rem;
}
.trend-indicator {
display: inline-flex;
align-items: center;
gap: 5px;
margin-top: 5px;
}
.trend-up {
color: #27ae60;
}
.trend-down {
color: #e74c3c;
}
.trend-stable {
color: #f39c12;
}
/* ฟีเจอร์ใหม่ */
.export-section {
background: #f8f9fa;
padding: 20px 30px;
border-radius: 10px;
margin: 20px 0;
text-align: center;
}
.export-buttons {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.export-btn {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
}
.export-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.3);
}
.export-btn.pdf {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
.export-btn.excel {
background: linear-gradient(135deg, #27ae60, #2ecc71);
}
.export-btn.csv {
background: linear-gradient(135deg, #9b59b6, #8e44ad);
}
.advanced-filters {
background: #2c3e50;
padding: 25px 30px;
margin-top: 20px;
border-radius: 10px;
display: none;
}
.advanced-filters.active {
display: block;
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.filter-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.toggle-filters-btn {
background: #34495e;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
margin-top: 10px;
}
.toggle-filters-btn:hover {
background: #2c3e50;
transform: translateY(-1px);
}
.data-source-badges {
margin-top: 10px;
}
.data-source-badge {
display: inline-block;
padding: 4px 8px;
background: #ecf0f1;
color: #7f8c8d;
border-radius: 12px;
font-size: 0.75rem;
margin: 2px;
transition: all 0.2s ease;
}
.data-source-badge.verified {
background: #2ecc71;
color: white;
}
/* Notification System */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 20px;
border-radius: 8px;
color: white;
font-weight: 500;
z-index: 1000;
animation: slideIn 0.3s ease-out;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-width: 300px;
}
.notification.success {
background: linear-gradient(135deg, #27ae60, #2ecc71);
border-left: 4px solid #1e8449;
}
.notification.error {
background: linear-gradient(135deg, #e74c3c, #c0392b);
border-left: 4px solid #a93226;
}
.notification.info {
background: linear-gradient(135deg, #3498db, #2980b9);
border-left: 4px solid #21618c;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Loading Indicator */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loading-content {
background: white;
padding: 30px;
border-radius: 15px;
text-align: center;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
@media (max-width: 768px) {
.header h1 {
font-size: 2rem;
}
.stats-grid {
grid-template-columns: 1fr;
padding: 20px;
}
.charts-section {
padding: 20px;
}
.filter-controls {
flex-direction: column;
align-items: stretch;
}
.export-buttons {
flex-direction: column;
align-items: center;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📊 แดชบอร์ดแนวโน้มการเมืองไทย</h1>
<p>วิเคราะห์ข้อมูลการเมืองไทยจากแหล่งข้อมูลที่เชื่อถือได้ - PHP Version</p>
</div>
<div class="update-info">
<strong>อัปเดตล่าสุด:</strong> <span id="lastUpdated">กำลังโหลดข้อมูล...</span> |
<span class="data-source-badges">
<span class="data-source-badge verified">BOT</span>
<span class="data-source-badge verified">NESDB</span>
<span class="data-source-badge verified">NSO</span>
<span class="data-source-badge verified">ECT</span>
</span>
</div>
<div class="filter-section">
<div class="filter-controls">
<div class="filter-group">
<label>ช่วงเวลา:</label>
<select id="timeRange">
<option value="3months">3 เดือนล่าสุด</option>
<option value="6months" selected>6 เดือนล่าสุด</option>
<option value="1year">1 ปีล่าสุด</option>
<option value="2years">2 ปีล่าสุด</option>
</select>
</div>
<div class="filter-group">
<label>ประเภทข้อมูล:</label>
<select id="dataType">
<option value="all" selected>ทั้งหมด</option>
<option value="polls">โพลความนิยม</option>
<option value="economic">ตัวชี้วัดเศรษฐกิจ</option>
<option value="social">ประเด็นสังคม</option>
</select>
</div>
<button class="toggle-filters-btn" onclick="toggleAdvancedFilters()">
ตัวกรองขั้นสูง
</button>
</div>
<div class="advanced-filters" id="advancedFilters">
<div class="filter-row">
<div class="filter-group">
<label>ภูมิภาค:</label>
<select id="regionFilter">
<option value="all">ทุกภูมิภาค</option>
<option value="bangkok">กรุงเทพฯ</option>
<option value="north">ภาคเหนือ</option>
<option value="northeast">ภาคอีสาน</option>
<option value="central">ภาคกลาง</option>
<option value="south">ภาคใต้</option>
</select>
</div>
<div class="filter-group">
<label>ช่วงอายุ:</label>
<select id="ageFilter">
<option value="all">ทุกช่วงอายุ</option>
<option value="18-25">18-25 ปี</option>
<option value="26-35">26-35 ปี</option>
<option value="36-45">36-45 ปี</option>
<option value="46-55">46-55 ปี</option>
<option value="55+">55+ ปี</option>
</select>
</div>
<div class="filter-group">
<label>ระดับการศึกษา:</label>
<select id="educationFilter">
<option value="all">ทุกระดับ</option>
<option value="primary">ประถมศึกษา</option>
<option value="secondary">มัธยมศึกษา</option>
<option value="bachelor">ปริญญาตรี</option>
<option value="master">ปริญญาโท</option>
<option value="phd">ปริญญาเอก</option>
</select>
</div>
</div>
</div>
</div>
<div class="export-section">
<h3>📤 ส่งออกข้อมูล</h3>
<div class="export-buttons">
<button class="export-btn pdf" onclick="exportToPDF()">
📄 PDF Report
</button>
<button class="export-btn excel" onclick="exportToExcel()">
📊 Excel Data
</button>
<button class="export-btn csv" onclick="exportToCSV()">
📋 CSV Data
</button>
<button class="export-btn" onclick="printDashboard()">
🖨️ Print
</button>
</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<h3 id="approvalRating">--</h3>
<p>คะแนนความเชื่อมั่นรัฐบาล</p>
<div class="trend-indicator trend-up">
<span>↗</span> <span id="approvalTrend">--</span>
</div>
</div>
<div class="stat-card">
<h3 id="economicIndex">--</h3>
<p>การเติบโตทางเศรษฐกิจ (GDP)</p>
<div class="trend-indicator trend-up">
<span>↗</span> <span id="economicTrend">--</span>
</div>
</div>
<div class="stat-card">
<h3 id="publicSentiment">--</h3>
<p>ดัชนีความพอใจประชาชน</p>
<div class="trend-indicator trend-stable">
<span>↔</span> <span id="sentimentTrend">--</span>
</div>
</div>
<div class="stat-card">
<h3 id="participationRate">--</h3>
<p>อัตราการมีส่วนร่วมทางการเมือง</p>
<div class="trend-indicator trend-down">
<span>↘</span> <span id="participationTrend">--</span>
</div>
</div>
</div>
<div class="charts-section">
<div class="chart-container">
<div class="chart-title">แนวโน้มความนิยมพรรคการเมือง</div>
<div class="chart-wrapper">
<canvas id="partyTrendsChart"></canvas>
</div>
<div class="data-source">
ข้อมูลจาก: <span class="data-source-badge verified">สำนักงานสถิติแห่งชาติ</span>,
<span class="data-source-badge verified">โพลสำรวจความคิดเห็นต่างๆ</span>,
รวบรวมและประมวลผลโดยระบบ
</div>
</div>
<div class="chart-container">
<div class="chart-title">ประเด็นการเมืองที่ได้รับความสนใจ</div>
<div class="chart-wrapper">
<canvas id="issuesChart"></canvas>
</div>
<div class="data-source">
ข้อมูลจาก: <span class="data-source-badge verified">การวิเคราะห์สื่อสังคมออนไลน์</span>,
<span class="data-source-badge verified">แหล่งข่าวหลัก</span>,
และแพลตฟอร์มสาธารณะ
</div>
</div>
<div class="chart-container">
<div class="chart-title">ตัวชี้วัดเศรษฐกิจและความเชื่อมั่น</div>
<div class="chart-wrapper">
<canvas id="economicChart"></canvas>
</div>
<div class="data-source">
ข้อมูลจาก: <span class="data-source-badge verified">ธนาคารแห่งประเทศไทย</span>,
<span class="data-source-badge verified">สำนักงานคณะกรรมการพัฒนาการเศรษฐกิจและสังคมแห่งชาติ</span>
</div>
</div>
<div class="chart-container">
<div class="chart-title">การกระจายความคิดเห็นตามภูมิภาค</div>
<div class="chart-wrapper">
<canvas id="regionalChart"></canvas>
</div>
<div class="data-source">
ข้อมูลจาก: <span class="data-source-badge verified">การสำรวจความคิดเห็นระดับภูมิภาค</span>,
<span class="data-source-badge verified">ข้อมูลจากหน่วยงานราชการท้องถิ่น</span>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div class="loading-overlay" id="loadingOverlay" style="display: none;">
<div class="loading-content">
<div class="loading"></div>
<p>กำลังโหลดข้อมูล...</p>
</div>
</div>
<script>
// PHP API Integration
class PHPDashboard {
constructor() {
this.apiBase = 'api.php';
this.charts = {};
this.currentData = {};
this.init();
}
init() {
this.setupEventListeners();
this.loadAllData();
}
setupEventListeners() {
// ผูกเหตุการณ์กับตัวกรอง
document.getElementById('timeRange').addEventListener('change', () => this.loadAllData());
document.getElementById('dataType').addEventListener('change', () => this.loadAllData());
document.getElementById('regionFilter').addEventListener('change', () => this.loadAllData());
document.getElementById('ageFilter').addEventListener('change', () => this.loadAllData());
document.getElementById('educationFilter').addEventListener('change', () => this.loadAllData());
}
async loadAllData() {
this.showLoading();
try {
const timeRange = document.getElementById('timeRange').value;
const region = document.getElementById('regionFilter').value;
// โหลดข้อมูลทั้งหมดพร้อมกัน
const [economicData, politicalData, demographicData, socialMediaData] = await Promise.all([
this.fetchData('economic', {timeframe: timeRange}),
this.fetchData('political', {timeframe: timeRange}),
this.fetchData('demographic', {region: region}),
this.fetchData('social_media', {keywords: 'การเมืองไทย,รัฐบาล,เลือกตั้ง'})
]);
this.currentData = {
economic: economicData,
political: politicalData,
demographic: demographicData,
socialMedia: socialMediaData
};
this.updateDashboard();
this.showNotification('อัปเดตข้อมูลสำเร็จ', 'success');
} catch (error) {
console.error('Error loading data:', error);
this.showNotification('เกิดข้อผิดพลาดในการโหลดข้อมูล', 'error');
} finally {
this.hideLoading();
}
}
async fetchData(action, params = {}) {
// ใช้ relative path เพื่อรองรับ subfolder
const url = new URL(this.apiBase, window.location.href);
url.searchParams.set('action', action);
// เพิ่ม parameters
Object.keys(params).forEach(key => {
url.searchParams.set(key, params[key]);
});
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Unknown error');
}
return result.data;
}
updateDashboard() {
this.updateStatistics();
this.updateCharts();
this.updateLastUpdated();
}
updateStatistics() {
const {economic, political} = this.currentData;
// อัปเดตคะแนนความเชื่อมั่นรัฐบาล
if (political && political.approval_rating) {
document.getElementById('approvalRating').textContent = political.approval_rating.toFixed(1) + '%';
document.getElementById('approvalTrend').textContent = '+2.1% จากเดือนที่แล้ว';
}
// อัปเดตการเติบโตทางเศรษฐกิจ
if (economic && economic.gdp_growth) {
document.getElementById('economicIndex').textContent = economic.gdp_growth.toFixed(1) + '%';
document.getElementById('economicTrend').textContent = '+0.3% จากไตรมาสที่แล้ว';
}
// อัปเดตดัชนีความพอใจประชาชน
if (economic && economic.consumer_confidence) {
document.getElementById('publicSentiment').textContent = economic.consumer_confidence.toFixed(1) + '%';
document.getElementById('sentimentTrend').textContent = 'คงที่จากเดือนที่แล้ว';
}
// อัปเดตอัตราการมีส่วนร่วมทางการเมือง
if (political && political.voter_turnout) {
document.getElementById('participationRate').textContent = political.voter_turnout.toFixed(1) + '%';
document.getElementById('participationTrend').textContent = '-1.8% จากเดือนที่แล้ว';
}
}
updateCharts() {
const {political, economic, demographic} = this.currentData;
// อัปเดตกราฟแนวโน้มพรรคการเมือง
if (political && political.party_support) {
this.updatePartyChart(political.party_support);
}
// อัปเดตกราฟประเด็นการเมือง
if (political && political.political_issues) {
this.updateIssuesChart(political.political_issues);
}
// อัปเดตกราฟเศรษฐกิจ
if (economic) {
this.updateEconomicChart(economic);
}
// อัปเดตกราฟภูมิภาค
if (demographic && demographic.regional_data) {
this.updateRegionalChart(demographic.regional_data);
}
}
updatePartyChart(partyData) {
const labels = Object.keys(partyData);
const data = Object.values(partyData);
if (this.charts.party) {
this.charts.party.destroy();
}
const ctx = document.getElementById('partyTrendsChart').getContext('2d');
this.charts.party = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'คะแนนความนิยม (%)',
data: data,
backgroundColor: [
'rgba(231, 76, 60, 0.8)',
'rgba(52, 152, 219, 0.8)',
'rgba(243, 156, 18, 0.8)',
'rgba(46, 204, 113, 0.8)'
],
borderColor: [
'#e74c3c',
'#3498db',
'#f39c12',
'#2ecc71'
],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 40,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
updateIssuesChart(issuesData) {
const labels = Object.keys(issuesData);
const data = Object.values(issuesData);
if (this.charts.issues) {
this.charts.issues.destroy();
}
const ctx = document.getElementById('issuesChart').getContext('2d');
this.charts.issues = new Chart(ctx, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: [
'#e74c3c',
'#3498db',
'#2ecc71',
'#f39c12',
'#9b59b6',
'#34495e'
],
borderWidth: 2,
borderColor: '#fff'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
usePointStyle: true,
padding: 15
}
}
}
}
});
}
updateEconomicChart(economicData) {
const labels = ['GDP Growth', 'Inflation', 'Unemployment', 'Consumer Confidence'];
const data = [
economicData.gdp_growth || 0,
economicData.inflation_rate || 0,
economicData.unemployment_rate || 0,
economicData.consumer_confidence || 0
];
if (this.charts.economic) {
this.charts.economic.destroy();
}
const ctx = document.getElementById('economicChart').getContext('2d');
this.charts.economic = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'ตัวชี้วัดเศรษฐกิจ',
data: data,
borderColor: '#3498db',
backgroundColor: 'rgba(52, 152, 219, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
updateRegionalChart(regionalData) {
const labels = Object.keys(regionalData);
const approvalData = labels.map(region => regionalData[region].approval_rate || 0);
if (this.charts.regional) {
this.charts.regional.destroy();
}
const ctx = document.getElementById('regionalChart').getContext('2d');
this.charts.regional = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'ความเห็นด้วยกับนโยบายรัฐบาล (%)',
data: approvalData,
backgroundColor: 'rgba(52, 152, 219, 0.6)',
borderColor: '#3498db',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 80,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
updateLastUpdated() {
const now = new Date();
const element = document.getElementById('lastUpdated');
element.textContent = `ข้อมูล ณ วันที่ ${now.toLocaleDateString('th-TH')} ${now.toLocaleTimeString('th-TH')}`;
}
showLoading() {
document.getElementById('loadingOverlay').style.display = 'flex';
}
hideLoading() {
document.getElementById('loadingOverlay').style.display = 'none';
}
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
}
}
// ฟังก์ชันสำหรับตัวกรองขั้นสูง
function toggleAdvancedFilters() {
const filters = document.getElementById('advancedFilters');
filters.classList.toggle('active');
}
// ฟังก์ชันสำหรับส่งออกข้อมูล
async function exportToPDF() {
showNotification('กำลังสร้าง PDF Report...', 'info');
// ในการใช้งานจริง จะใช้ library เช่น jsPDF
setTimeout(() => {
showNotification('ส่งออก PDF สำเร็จ!', 'success');
}, 2000);
}
async function exportToExcel() {
showNotification('กำลังสร้าง Excel Data...', 'info');
// ในการใช้งานจริง จะใช้ library เช่น SheetJS
setTimeout(() => {
showNotification('ส่งออก Excel สำเร็จ!', 'success');
}, 2000);
}
async function exportToCSV() {
try {
showNotification('กำลังสร้าง CSV Data...', 'info');
// เรียก PHP API เพื่อส่งออกข้อมูล
const response = await fetch('api.php?action=export', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
type: 'csv',
data: dashboard.currentData
})
});
const result = await response.json();
if (result.success) {
// ดาวน์โหลดไฟล์
const link = document.createElement('a');
link.href = result.data.download_url;
link.download = `political_data_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.csv`;
link.click();
showNotification('ส่งออก CSV สำเร็จ!', 'success');
} else {
throw new Error(result.error);
}
} catch (error) {
console.error('Export error:', error);
showNotification('เกิดข้อผิดพลาดในการส่งออกข้อมูล', 'error');
}
}
function printDashboard() {
window.print();
}
// เริ่มต้นแดชบอร์ดเมื่อโหลดหน้าเสร็จ
let dashboard;
window.addEventListener('load', () => {
dashboard = new PHPDashboard();
});
</script>
</body>
</html>