Fix dynamic API base path for server deployment

This commit is contained in:
pongpon pitisuk
2026-09-12 14:34:09 +07:00
parent 72eb7cd0c8
commit e01d01464a
9 changed files with 262 additions and 52 deletions
+27 -18
View File
@@ -5,10 +5,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TPU Survey - Admin Dashboard</title> <title>TPU Survey - Admin Dashboard</title>
<meta name="description" content="แดชบอร์ดผู้ดูแลระบบ สำหรับจัดการแบบสอบถามและผู้ใช้ คณะนวัตกรรมดิจิทัลเทคโนโลยี มหาวิทยาลัยตาปี"> <meta name="description"
content="แดชบอร์ดผู้ดูแลระบบ สำหรับจัดการแบบสอบถามและผู้ใช้ คณะนวัตกรรมดิจิทัลเทคโนโลยี มหาวิทยาลัยตาปี">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script> <script>
@@ -37,11 +39,10 @@
<!-- Sidebar --> <!-- Sidebar -->
<aside class="sidebar"> <aside class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<img src="pic/logo.png" alt="TPU Logo" class="sidebar-logo-img" <img src="pic/logo.png" alt="TPU Logo" class="sidebar-logo-img" onerror="this.style.display='none'">
onerror="this.style.display='none'">
<div> <div>
<h2 class="text-white font-bold text-sm leading-tight">คณะนวัตกรรมดิจิทัลเทคโนโลยี</h2>
<span class="text-yellow-300 text-xs font-medium">มหาวิทยาลัยตาปี</span> <span class="text-white font-bold text-sm leading-tight">มหาวิทยาลัยตาปี</span>
</div> </div>
</div> </div>
@@ -157,8 +158,8 @@
<div class="status-chart-row"> <div class="status-chart-row">
<div class="status-chart-label">📝 ร่าง</div> <div class="status-chart-label">📝 ร่าง</div>
<div class="status-chart-bar-track"> <div class="status-chart-bar-track">
<div class="status-chart-bar-fill draft" id="chartDraft" <div class="status-chart-bar-fill draft" id="chartDraft" style="width: 0%">
style="width: 0%"></div> </div>
</div> </div>
<div class="status-chart-count" id="chartDraftCount">0</div> <div class="status-chart-count" id="chartDraftCount">0</div>
</div> </div>
@@ -233,9 +234,8 @@
<button class="filter-tab" data-filter="active" <button class="filter-tab" data-filter="active"
onclick="filterSurveys('active', this)">🟢 เปิด <span class="tab-count" onclick="filterSurveys('active', this)">🟢 เปิด <span class="tab-count"
id="countActive">0</span></button> id="countActive">0</span></button>
<button class="filter-tab" data-filter="draft" <button class="filter-tab" data-filter="draft" onclick="filterSurveys('draft', this)">📝
onclick="filterSurveys('draft', this)">📝 ร่าง <span class="tab-count" ร่าง <span class="tab-count" id="countDraft">0</span></button>
id="countDraft">0</span></button>
<button class="filter-tab" data-filter="closed" <button class="filter-tab" data-filter="closed"
onclick="filterSurveys('closed', this)">🔴 ปิด <span class="tab-count" onclick="filterSurveys('closed', this)">🔴 ปิด <span class="tab-count"
id="countClosed">0</span></button> id="countClosed">0</span></button>
@@ -247,8 +247,11 @@
<input type="text" id="surveySearchInput" placeholder="ค้นหาแบบสอบถาม..." <input type="text" id="surveySearchInput" placeholder="ค้นหาแบบสอบถาม..."
oninput="filterSurveysBySearch()"> oninput="filterSurveysBySearch()">
</div> </div>
<button class="btn btn-primary btn-sm" onclick="showCreateSurveyModal()">➕ สร้างแบบสอบถาม</button> <button class="btn btn-primary btn-sm" onclick="showCreateSurveyModal()">➕
<button class="btn btn-primary btn-sm" style="background-color:#16a34a;border-color:#16a34a;" onclick="showImportGoogleFormModal()">📥 นำเข้าจาก Google Forms</button> สร้างแบบสอบถาม</button>
<button class="btn btn-primary btn-sm"
style="background-color:#16a34a;border-color:#16a34a;"
onclick="showImportGoogleFormModal()">📥 นำเข้าจาก Google Forms</button>
</div> </div>
</div> </div>
@@ -277,7 +280,9 @@
<!-- Profile Card --> <!-- Profile Card -->
<div class="card" style="text-align:center;"> <div class="card" style="text-align:center;">
<div class="card-body" style="padding:32px 24px;"> <div class="card-body" style="padding:32px 24px;">
<div id="profileAvatar" style="width:80px;height:80px;border-radius:50%;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">A</div> <div id="profileAvatar"
style="width:80px;height:80px;border-radius:50%;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">
A</div>
<h3 id="profileDisplayName" style="margin-bottom:4px;">—</h3> <h3 id="profileDisplayName" style="margin-bottom:4px;">—</h3>
<span class="text-muted text-sm" id="profileDisplayRole">ผู้ดูแลระบบ</span> <span class="text-muted text-sm" id="profileDisplayRole">ผู้ดูแลระบบ</span>
<div style="margin-top:12px;"> <div style="margin-top:12px;">
@@ -285,7 +290,8 @@
</div> </div>
<hr style="border-color:var(--border-color);margin:20px 0;"> <hr style="border-color:var(--border-color);margin:20px 0;">
<div class="text-sm text-muted">🆔 รหัสบุคลากร</div> <div class="text-sm text-muted">🆔 รหัสบุคลากร</div>
<div style="font-size:18px;font-weight:600;margin-top:4px;" id="profilePersonnelCode">—</div> <div style="font-size:18px;font-weight:600;margin-top:4px;" id="profilePersonnelCode">—
</div>
</div> </div>
</div> </div>
@@ -300,7 +306,8 @@
<label class="form-label">รหัสบุคลากร</label> <label class="form-label">รหัสบุคลากร</label>
<input type="text" class="form-input" id="profileCode" disabled <input type="text" class="form-input" id="profileCode" disabled
style="background:var(--bg-secondary);cursor:not-allowed;opacity:0.7;"> style="background:var(--bg-secondary);cursor:not-allowed;opacity:0.7;">
<small class="text-muted" style="display:block;margin-top:4px;">🔒 ไม่สามารถแก้ไขได้ — ติดต่อผู้ดูแลระบบ</small> <small class="text-muted" style="display:block;margin-top:4px;">🔒
ไม่สามารถแก้ไขได้ — ติดต่อผู้ดูแลระบบ</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">ชื่อ-นามสกุล *</label> <label class="form-label">ชื่อ-นามสกุล *</label>
@@ -335,8 +342,10 @@
placeholder="พิมพ์รหัสผ่านใหม่อีกครั้ง"> placeholder="พิมพ์รหัสผ่านใหม่อีกครั้ง">
</div> </div>
<div style="display:flex;gap:12px;justify-content:flex-end;margin-top:24px;"> <div style="display:flex;gap:12px;justify-content:flex-end;margin-top:24px;">
<button type="button" class="btn btn-secondary" onclick="loadProfile()">🔄 รีเซ็ต</button> <button type="button" class="btn btn-secondary" onclick="loadProfile()">🔄
<button type="button" class="btn btn-primary" onclick="saveProfile()">💾 บันทึกข้อมูล</button> รีเซ็ต</button>
<button type="button" class="btn btn-primary" onclick="saveProfile()">💾
บันทึกข้อมูล</button>
</div> </div>
</form> </form>
</div> </div>
+9 -6
View File
@@ -5,10 +5,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TPU Survey - ระบบประเมินความพึงพอใจ</title> <title>TPU Survey - ระบบประเมินความพึงพอใจ</title>
<meta name="description" content="ระบบแบบสอบถามประเมินผลโครงการ คณะนวัตกรรมดิจิทัลเทคโนโลยี มหาวิทยาลัยตาปี"> <meta name="description" content="ระบบแบบสอบถามประเมินผล มหาวิทยาลัยตาปี">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script> <script>
@@ -33,13 +34,14 @@
<div class="login-page"> <div class="login-page">
<div class="login-card glass-card relative overflow-hidden"> <div class="login-card glass-card relative overflow-hidden">
<!-- Accent Gold Line --> <!-- Accent Gold Line -->
<div class="h-1.5 bg-gradient-to-r from-yellow-300 via-yellow-400 to-yellow-500 absolute top-0 left-0 right-0"></div> <div
class="h-1.5 bg-gradient-to-r from-yellow-300 via-yellow-400 to-yellow-500 absolute top-0 left-0 right-0">
</div>
<div class="login-logo"> <div class="login-logo">
<img src="pic/logo.png" alt="TPU Logo" class="h-28 w-auto mx-auto mb-4 drop-shadow-md"> <img src="pic/logo.png" alt="TPU Logo" class="h-28 w-auto mx-auto mb-4 drop-shadow-md">
<h1 style="color: #1e3a8a; font-size: 2rem; font-weight: 800; margin-bottom: 4px;">TPU Survey</h1> <h1 style="color: #1e3a8a; font-size: 2rem; font-weight: 800; margin-bottom: 4px;">TPU Survey</h1>
<p class="text-blue-900 font-bold text-sm">คณะนวัตกรรมดิจิทัลเทคโนโลยี</p> <p class="text-gray-500 text-xs mt-0.5">มหาวิทยาลัยตาปี (Tapee University)</p>
<p class="text-gray-500 text-xs mt-0.5">มหาวิทยาลัยตาปี (Tapi University)</p>
</div> </div>
<form id="loginForm" onsubmit="handleLogin(event)"> <form id="loginForm" onsubmit="handleLogin(event)">
@@ -60,7 +62,8 @@
<div id="loginError" class="text-danger text-sm mb-16" style="display:none;"></div> <div id="loginError" class="text-danger text-sm mb-16" style="display:none;"></div>
<button type="submit" id="loginBtn" class="btn btn-primary btn-block btn-lg shadow-lg hover:shadow-xl transition-all"> <button type="submit" id="loginBtn"
class="btn btn-primary btn-block btn-lg shadow-lg hover:shadow-xl transition-all">
<i class="fas fa-sign-in-alt mr-2"></i> เข้าสู่ระบบ <i class="fas fa-sign-in-alt mr-2"></i> เข้าสู่ระบบ
</button> </button>
</form> </form>
+2 -1
View File
@@ -798,7 +798,8 @@ async function changeStatus(id, status) {
async function generateLink(id) { async function generateLink(id) {
try { try {
const data = await API.post('surveys.php?action=generateLink', { id }); const data = await API.post('surveys.php?action=generateLink', { id });
const fullUrl = data.full_url || `${window.location.origin}/survey/respond.php?link=${data.unique_link}`; const basePath = window.location.pathname.includes('/survey') ? '/survey' : '';
const fullUrl = data.full_url || `${window.location.origin}${basePath}/respond.php?link=${data.unique_link}`;
// Find survey title // Find survey title
const survey = surveysList.find(s => s.id == id); const survey = surveysList.find(s => s.id == id);
+6 -2
View File
@@ -3,8 +3,12 @@
* API communication, Auth state, Toast notifications, Theme * API communication, Auth state, Toast notifications, Theme
*/ */
const getAppBasePath = () => {
return window.location.pathname.includes('/survey') ? '/survey' : '';
};
const API = { const API = {
base: '/survey/api', base: `${getAppBasePath()}/api`,
async request(endpoint, options = {}) { async request(endpoint, options = {}) {
const url = `${this.base}/${endpoint}`; const url = `${this.base}/${endpoint}`;
@@ -66,7 +70,7 @@ const Auth = {
async logout() { async logout() {
await API.post('auth.php?action=logout', {}); await API.post('auth.php?action=logout', {});
this.user = null; this.user = null;
window.location.href = '/survey/index.html'; window.location.href = `${getAppBasePath()}/index.html`;
}, },
isAdmin() { return this.user?.role === 'admin'; }, isAdmin() { return this.user?.role === 'admin'; },
+4 -3
View File
@@ -76,7 +76,7 @@ function renderEditor() {
<input type="hidden" id="edit-survey-cert-template-path" value="${escapeHtml(currentSurvey.certificate_template || '')}"> <input type="hidden" id="edit-survey-cert-template-path" value="${escapeHtml(currentSurvey.certificate_template || '')}">
<div id="cert-preview-wrapper" style="margin-top:12px; display:${currentSurvey.certificate_template ? 'block' : 'none'};"> <div id="cert-preview-wrapper" style="margin-top:12px; display:${currentSurvey.certificate_template ? 'block' : 'none'};">
<img id="cert-template-preview" src="${currentSurvey.certificate_template ? '/survey/' + currentSurvey.certificate_template : ''}" style="max-height:140px; width:auto; border-radius:8px; border:1px solid var(--border-color); box-shadow:var(--shadow-sm);"> <img id="cert-template-preview" src="${currentSurvey.certificate_template ? (window.location.pathname.includes('/survey') ? '/survey/' : '/') + currentSurvey.certificate_template : ''}" style="max-height:140px; width:auto; border-radius:8px; border:1px solid var(--border-color); box-shadow:var(--shadow-sm);">
</div> </div>
</div> </div>
@@ -486,7 +486,8 @@ async function uploadCertTemplate(input) {
formData.append('template_image', file); formData.append('template_image', file);
try { try {
const res = await fetch('/survey/api/surveys.php?action=uploadTemplate', { const basePath = window.location.pathname.includes('/survey') ? '/survey' : '';
const res = await fetch(`${basePath}/api/surveys.php?action=uploadTemplate`, {
method: 'POST', method: 'POST',
body: formData body: formData
}); });
@@ -501,7 +502,7 @@ async function uploadCertTemplate(input) {
// Update preview // Update preview
const previewImg = document.getElementById('cert-template-preview'); const previewImg = document.getElementById('cert-template-preview');
const previewWrapper = document.getElementById('cert-preview-wrapper'); const previewWrapper = document.getElementById('cert-preview-wrapper');
previewImg.src = '/survey/' + data.template_path; previewImg.src = (basePath ? `${basePath}/` : '/') + data.template_path;
previewWrapper.style.display = 'block'; previewWrapper.style.display = 'block';
// Add dynamic delete button if it doesn't exist // Add dynamic delete button if it doesn't exist
+4 -2
View File
@@ -15,7 +15,8 @@ async function initSurvey() {
} }
try { try {
const data = await fetch(`/survey/api/surveys.php?action=getByLink&link=${encodeURIComponent(link)}`).then(r => r.json()); const basePath = window.location.pathname.includes('/survey') ? '/survey' : '';
const data = await fetch(`${basePath}/api/surveys.php?action=getByLink&link=${encodeURIComponent(link)}`).then(r => r.json());
if (data.error) { if (data.error) {
showError(data.error); showError(data.error);
@@ -249,7 +250,8 @@ async function submitSurveyResponse(e) {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
try { try {
const res = await fetch('/survey/api/responses.php?action=submit', { const basePath = window.location.pathname.includes('/survey') ? '/survey' : '';
const res = await fetch(`${basePath}/api/responses.php?action=submit`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
+2 -1
View File
@@ -374,7 +374,8 @@ async function teacherDuplicateSurvey(id) {
async function teacherGenerateLink(id) { async function teacherGenerateLink(id) {
try { try {
const data = await API.post('surveys.php?action=generateLink', { id }); const data = await API.post('surveys.php?action=generateLink', { id });
const fullUrl = data.full_url || `${window.location.origin}/survey/respond.php?link=${data.unique_link}`; const basePath = window.location.pathname.includes('/survey') ? '/survey' : '';
const fullUrl = data.full_url || `${window.location.origin}${basePath}/respond.php?link=${data.unique_link}`;
// Find survey title // Find survey title
const survey = teacherSurveys.find(s => s.id == id); const survey = teacherSurveys.find(s => s.id == id);
+175
View File
@@ -0,0 +1,175 @@
#!/bin/bash
# ==============================================================================
# Automated Server Setup Script for TPU Survey (survey.tapee.ac.th)
# Compatible with Ubuntu 20.04 / 22.04 / 24.04 and Debian 11 / 12
# ==============================================================================
set -e
# Configuration Variables
DOMAIN="survey.tapee.ac.th"
WEB_ROOT="/var/www/survey"
DB_NAME="tpu_survey_v2"
DB_USER="survey_user"
DB_PASS="SurveyTPU2026!Secure" # Change this if needed
GIT_REPO="https://git.tapee.ac.th/pongpon/survey.git"
echo "=========================================================="
echo " Starting Setup for ${DOMAIN} on VM 110"
echo "=========================================================="
# 1. Update system packages
echo "[1/6] Updating system packages..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -y && apt-get upgrade -y
# 2. Install Web Server, PHP, and MariaDB
echo "[2/6] Installing Apache, PHP, MariaDB, and utilities..."
apt-get install -y \
apache2 \
mariadb-server \
mariadb-client \
git \
curl \
unzip \
certbot \
python3-certbot-apache \
php \
php-cli \
php-fpm \
php-mysql \
php-mbstring \
php-xml \
php-curl \
php-gd \
php-zip \
php-intl \
libapache2-mod-php
# Enable Apache Modules
a2enmod rewrite ssl headers
# 3. Setup MariaDB Database
echo "[3/6] Configuring Database (${DB_NAME})..."
systemctl start mariadb
systemctl enable mariadb
mysql -u root <<EOF
CREATE DATABASE IF NOT EXISTS \`${DB_NAME}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON \`${DB_NAME}\`.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF
echo "Database and User created successfully."
# 4. Deploy Project Source Code
echo "[4/6] Setting up project files at ${WEB_ROOT}..."
if [ -d "${WEB_ROOT}/.git" ]; then
echo "Updating existing repository..."
cd "${WEB_ROOT}"
git pull origin main || true
else
mkdir -p "${WEB_ROOT}"
if [ -f "./tpu_survey_v2.sql" ]; then
echo "Copying local files to ${WEB_ROOT}..."
cp -r ./* "${WEB_ROOT}/"
else
echo "Cloning from Git repository..."
git clone "${GIT_REPO}" "${WEB_ROOT}" || true
fi
fi
# Import Database Schema if SQL file exists
if [ -f "${WEB_ROOT}/tpu_survey_v2.sql" ]; then
echo "Importing database schema from tpu_survey_v2.sql..."
mysql -u root "${DB_NAME}" < "${WEB_ROOT}/tpu_survey_v2.sql" || echo "Note: Initial import completed or tables exist."
fi
# Update config/database.php with the server credentials
cat <<EOF > "${WEB_ROOT}/config/database.php"
<?php
/**
* Database Configuration
* TPU Survey System
*/
define('DB_HOST', 'localhost');
define('DB_NAME', '${DB_NAME}');
define('DB_USER', '${DB_USER}');
define('DB_PASS', '${DB_PASS}');
define('DB_CHARSET', 'utf8mb4');
function getDB() {
static \$pdo = null;
if (\$pdo === null) {
try {
\$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
\$pdo = new PDO(\$dsn, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
} catch (PDOException \$e) {
http_response_code(500);
echo json_encode(['error' => 'Database connection failed: ' . \$e->getMessage()]);
exit;
}
}
return \$pdo;
}
function jsonResponse(\$data, \$code = 200) {
http_response_code(\$code);
header('Content-Type: application/json; charset=utf-8');
echo json_encode(\$data, JSON_UNESCAPED_UNICODE);
exit;
}
function getInput() {
\$json = file_get_contents('php://input');
return json_decode(\$json, true) ?: [];
}
EOF
# Set proper permissions
mkdir -p "${WEB_ROOT}/uploads" "${WEB_ROOT}/temp_certificates"
chown -R www-data:www-data "${WEB_ROOT}"
chmod -R 755 "${WEB_ROOT}"
chmod -R 775 "${WEB_ROOT}/uploads" "${WEB_ROOT}/temp_certificates"
# 5. Configure Apache VirtualHost
echo "[5/6] Creating Apache VirtualHost for ${DOMAIN}..."
cat <<EOF > /etc/apache2/sites-available/${DOMAIN}.conf
<VirtualHost *:80>
ServerName ${DOMAIN}
ServerAlias www.${DOMAIN}
DocumentRoot ${WEB_ROOT}
<Directory ${WEB_ROOT}>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/${DOMAIN}_error.log
CustomLog \${APACHE_LOG_DIR}/${DOMAIN}_access.log combined
</VirtualHost>
EOF
a2dissite 000-default.conf || true
a2ensite ${DOMAIN}.conf
systemctl restart apache2
# 6. Summary & SSL Instructions
echo "=========================================================="
echo " SETUP COMPLETED SUCCESSFULLY!"
echo "=========================================================="
echo "1. Web Root: ${WEB_ROOT}"
echo "2. Database Name: ${DB_NAME}"
echo "3. Database User: ${DB_USER}"
echo "4. VirtualHost: http://${DOMAIN}"
echo ""
echo "To enable HTTPS (SSL Certificate via Let's Encrypt), run:"
echo " certbot --apache -d ${DOMAIN}"
echo "=========================================================="
+27 -13
View File
@@ -5,10 +5,12 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TPU Survey - อาจารย์/เจ้าของโครงการ</title> <title>TPU Survey - อาจารย์/เจ้าของโครงการ</title>
<meta name="description" content="หน้าจัดการแบบสอบถามสำหรับอาจารย์และเจ้าของโครงการ คณะนวัตกรรมดิจิทัลเทคโนโลยี มหาวิทยาลัยตาปี"> <meta name="description"
content="หน้าจัดการแบบสอบถามสำหรับอาจารย์และเจ้าของโครงการ คณะนวัตกรรมดิจิทัลเทคโนโลยี มหาวิทยาลัยตาปี">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
<script> <script>
@@ -37,8 +39,8 @@
<div class="sidebar-header"> <div class="sidebar-header">
<img src="pic/logo.png" alt="TPU Logo" class="sidebar-logo-img"> <img src="pic/logo.png" alt="TPU Logo" class="sidebar-logo-img">
<div> <div>
<h2 class="text-white font-bold text-sm leading-tight">คณะนวัตกรรมดิจิทัลเทคโนโลยี</h2>
<small class="text-yellow-300 font-medium">มหาวิทยาลัยตาปี</small> <span class="text-white font-bold text-sm leading-tight">มหาวิทยาลัยตาปี</span>
</div> </div>
</div> </div>
@@ -83,14 +85,20 @@
<div id="view-mySurveys" class="view-section space-y-4"> <div id="view-mySurveys" class="view-section space-y-4">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4"> <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4">
<div> <div>
<h2 class="text-xl font-bold text-blue-900"><i class="fas fa-tasks mr-2 text-yellow-500"></i>แบบสอบถามของฉัน</h2> <h2 class="text-xl font-bold text-blue-900"><i
<p class="text-xs text-gray-500 font-medium">จัดการและตรวจสอบแบบสอบถามประเมินผลโครงการในระบบ</p> class="fas fa-tasks mr-2 text-yellow-500"></i>แบบสอบถามของฉัน</h2>
<p class="text-xs text-gray-500 font-medium">จัดการและตรวจสอบแบบสอบถามประเมินผลโครงการในระบบ
</p>
</div> </div>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<button class="btn-yellow px-4 py-2 rounded text-xs font-bold shadow-sm flex items-center gap-1.5 transition-all" onclick="showTeacherCreateModal()"> <button
class="btn-yellow px-4 py-2 rounded text-xs font-bold shadow-sm flex items-center gap-1.5 transition-all"
onclick="showTeacherCreateModal()">
<i class="fas fa-plus"></i> สร้างแบบสอบถาม <i class="fas fa-plus"></i> สร้างแบบสอบถาม
</button> </button>
<button class="bg-green-600 hover:bg-green-700 text-white border border-black px-4 py-2 rounded text-xs font-bold shadow-sm flex items-center gap-1.5 transition-all" onclick="showImportGoogleFormModal()"> <button
class="bg-green-600 hover:bg-green-700 text-white border border-black px-4 py-2 rounded text-xs font-bold shadow-sm flex items-center gap-1.5 transition-all"
onclick="showImportGoogleFormModal()">
<i class="fas fa-file-import"></i> นำเข้าจาก Google Forms <i class="fas fa-file-import"></i> นำเข้าจาก Google Forms
</button> </button>
</div> </div>
@@ -116,7 +124,9 @@
<!-- Profile Card --> <!-- Profile Card -->
<div class="card" style="text-align:center;"> <div class="card" style="text-align:center;">
<div class="card-body" style="padding:32px 24px;"> <div class="card-body" style="padding:32px 24px;">
<div id="profileAvatar" style="width:80px;height:80px;border-radius:50%;background:linear-gradient(135deg,#e8a838,#f0c040);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">T</div> <div id="profileAvatar"
style="width:80px;height:80px;border-radius:50%;background:linear-gradient(135deg,#e8a838,#f0c040);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 16px;">
T</div>
<h3 id="profileDisplayName" style="margin-bottom:4px;">—</h3> <h3 id="profileDisplayName" style="margin-bottom:4px;">—</h3>
<span class="text-muted text-sm" id="profileDisplayRole">อาจารย์</span> <span class="text-muted text-sm" id="profileDisplayRole">อาจารย์</span>
<div style="margin-top:12px;"> <div style="margin-top:12px;">
@@ -124,7 +134,8 @@
</div> </div>
<hr style="border-color:var(--border-color);margin:20px 0;"> <hr style="border-color:var(--border-color);margin:20px 0;">
<div class="text-sm text-muted">🆔 รหัสบุคลากร</div> <div class="text-sm text-muted">🆔 รหัสบุคลากร</div>
<div style="font-size:18px;font-weight:600;margin-top:4px;" id="profilePersonnelCode">—</div> <div style="font-size:18px;font-weight:600;margin-top:4px;" id="profilePersonnelCode">—
</div>
</div> </div>
</div> </div>
@@ -139,7 +150,8 @@
<label class="form-label">รหัสบุคลากร</label> <label class="form-label">รหัสบุคลากร</label>
<input type="text" class="form-input" id="profileCode" disabled <input type="text" class="form-input" id="profileCode" disabled
style="background:var(--bg-secondary);cursor:not-allowed;opacity:0.7;"> style="background:var(--bg-secondary);cursor:not-allowed;opacity:0.7;">
<small class="text-muted" style="display:block;margin-top:4px;">🔒 ไม่สามารถแก้ไขได้ — ติดต่อผู้ดูแลระบบ</small> <small class="text-muted" style="display:block;margin-top:4px;">🔒
ไม่สามารถแก้ไขได้ — ติดต่อผู้ดูแลระบบ</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">ชื่อ-นามสกุล *</label> <label class="form-label">ชื่อ-นามสกุล *</label>
@@ -174,8 +186,10 @@
placeholder="พิมพ์รหัสผ่านใหม่อีกครั้ง"> placeholder="พิมพ์รหัสผ่านใหม่อีกครั้ง">
</div> </div>
<div style="display:flex;gap:12px;justify-content:flex-end;margin-top:24px;"> <div style="display:flex;gap:12px;justify-content:flex-end;margin-top:24px;">
<button type="button" class="btn btn-secondary" onclick="loadProfile()">🔄 รีเซ็ต</button> <button type="button" class="btn btn-secondary" onclick="loadProfile()">🔄
<button type="button" class="btn btn-primary" onclick="saveProfile()">💾 บันทึกข้อมูล</button> รีเซ็ต</button>
<button type="button" class="btn btn-primary" onclick="saveProfile()">💾
บันทึกข้อมูล</button>
</div> </div>
</form> </form>
</div> </div>