Finish basic API implementation

This commit is contained in:
2025-08-27 15:01:06 +09:00
parent fff9bce9e7
commit cc9edb9932
19 changed files with 3844 additions and 5 deletions

View File

@ -0,0 +1,332 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ロケーションチェックイン テスト</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button:hover {
background-color: #0056b3;
}
.result {
margin-top: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #f8f9fa;
}
.error {
color: red;
}
.success {
color: green;
}
.location-info {
background-color: #e9ecef;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
}
.interaction-requirements {
background-color: #fff3cd;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
border-left: 4px solid #ffc107;
}
</style>
</head>
<body>
<h1>ロケーションチェックイン テスト</h1>
<div class="form-group">
<label for="locationSelect">ロケーション選択:</label>
<select id="locationSelect" onchange="updateLocationInfo()">
<option value="">ロケーションを選択してください</option>
</select>
</div>
<div id="locationInfo" class="location-info" style="display:none;">
<h3>ロケーション情報</h3>
<div id="locationDetails"></div>
<div id="interactionRequirements" class="interaction-requirements"></div>
</div>
<form id="checkinForm" onsubmit="submitCheckin(event)">
<div class="form-group">
<label for="latitude">緯度:</label>
<input type="number" id="latitude" step="any" required>
</div>
<div class="form-group">
<label for="longitude">経度:</label>
<input type="number" id="longitude" step="any" required>
</div>
<div id="photoGroup" class="form-group" style="display:none;">
<label for="photo">写真撮影 (Base64):</label>
<input type="file" id="photoFile" accept="image/*" onchange="handlePhotoUpload()">
<textarea id="photo" rows="3" placeholder="Base64エンコードされた写真データ"></textarea>
</div>
<div id="qrGroup" class="form-group" style="display:none;">
<label for="qrCodeData">QRコードデータ:</label>
<textarea id="qrCodeData" rows="3" placeholder='{"quiz_id": 1, "correct_answer": "正解"}'></textarea>
</div>
<div id="quizGroup" class="form-group" style="display:none;">
<label for="quizAnswer">クイズ回答:</label>
<input type="text" id="quizAnswer" placeholder="回答を入力してください">
</div>
<button type="submit">チェックイン実行</button>
</form>
<div id="result" class="result" style="display:none;"></div>
<script>
let locations = [];
// 初期化
document.addEventListener('DOMContentLoaded', function() {
loadLocations();
getCurrentLocation();
});
// ロケーション一覧を取得
async function loadLocations() {
try {
const response = await fetch('/api/inbound2');
const data = await response.json();
locations = data.features || [];
const select = document.getElementById('locationSelect');
select.innerHTML = '<option value="">ロケーションを選択してください</option>';
locations.forEach((location, index) => {
const props = location.properties;
const option = document.createElement('option');
option.value = index;
option.textContent = `${props.location_name} (ID: ${props.id}) - evaluation_value: ${props.evaluation_value || '0'}`;
select.appendChild(option);
});
} catch (error) {
console.error('ロケーション取得エラー:', error);
showResult('ロケーション一覧の取得に失敗しました', 'error');
}
}
// 現在地を取得
function getCurrentLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
document.getElementById('latitude').value = position.coords.latitude;
document.getElementById('longitude').value = position.coords.longitude;
});
}
}
// ロケーション情報を更新
function updateLocationInfo() {
const select = document.getElementById('locationSelect');
const index = select.value;
if (index === '') {
document.getElementById('locationInfo').style.display = 'none';
hideInteractionInputs();
return;
}
const location = locations[index];
const props = location.properties;
const evaluationValue = props.evaluation_value || '0';
// ロケーション詳細表示
const detailsDiv = document.getElementById('locationDetails');
detailsDiv.innerHTML = `
<p><strong>名前:</strong> ${props.location_name}</p>
<p><strong>ID:</strong> ${props.id}</p>
<p><strong>評価値:</strong> ${evaluationValue}</p>
<p><strong>カテゴリ:</strong> ${props.sub_category_name || 'N/A'}</p>
<p><strong>説明:</strong> ${props.text || 'N/A'}</p>
`;
// インタラクション要件表示
const requirementsDiv = document.getElementById('interactionRequirements');
let requirementText = '';
switch (evaluationValue) {
case '0':
requirementText = '📍 通常ポイント: 位置情報のみでチェックイン可能';
hideInteractionInputs();
break;
case '1':
requirementText = '📸 写真撮影ポイント: 写真撮影が必要(買い物ポイント)';
showPhotoInput();
break;
case '2':
requirementText = '🔍 QRコードポイント: QRコードスキャンとクイズ回答が必要';
showQRInput();
break;
default:
requirementText = '❓ 不明な評価値: 通常ポイントとして処理';
hideInteractionInputs();
break;
}
requirementsDiv.textContent = requirementText;
document.getElementById('locationInfo').style.display = 'block';
}
// インタラクション入力フィールドの表示制御
function hideInteractionInputs() {
document.getElementById('photoGroup').style.display = 'none';
document.getElementById('qrGroup').style.display = 'none';
document.getElementById('quizGroup').style.display = 'none';
}
function showPhotoInput() {
hideInteractionInputs();
document.getElementById('photoGroup').style.display = 'block';
}
function showQRInput() {
hideInteractionInputs();
document.getElementById('qrGroup').style.display = 'block';
document.getElementById('quizGroup').style.display = 'block';
}
// 写真アップロード処理
function handlePhotoUpload() {
const file = document.getElementById('photoFile').files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
const base64 = e.target.result.split(',')[1]; // data:image/jpeg;base64, を除去
document.getElementById('photo').value = base64;
};
reader.readAsDataURL(file);
}
}
// チェックイン実行
async function submitCheckin(event) {
event.preventDefault();
const select = document.getElementById('locationSelect');
const index = select.value;
if (index === '') {
showResult('ロケーションを選択してください', 'error');
return;
}
const location = locations[index];
const locationId = location.properties.id;
const evaluationValue = location.properties.evaluation_value || '0';
const data = {
location_id: locationId,
latitude: parseFloat(document.getElementById('latitude').value),
longitude: parseFloat(document.getElementById('longitude').value)
};
// evaluation_valueに応じたデータ追加
if (evaluationValue === '1') {
const photo = document.getElementById('photo').value;
if (!photo) {
showResult('写真撮影が必要です', 'error');
return;
}
data.photo = photo;
} else if (evaluationValue === '2') {
const qrData = document.getElementById('qrCodeData').value;
const quizAnswer = document.getElementById('quizAnswer').value;
if (!qrData || !quizAnswer) {
showResult('QRコードデータとクイズ回答が必要です', 'error');
return;
}
data.qr_code_data = qrData;
data.quiz_answer = quizAnswer;
}
try {
const response = await fetch('/api/location-checkin/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
showResult(`チェックイン成功!\n${JSON.stringify(result, null, 2)}`, 'success');
} else {
showResult(`チェックイン失敗: ${result.error}\n${JSON.stringify(result, null, 2)}`, 'error');
}
} catch (error) {
console.error('チェックインエラー:', error);
showResult('チェックイン処理でエラーが発生しました', 'error');
}
}
// 結果表示
function showResult(message, type) {
const resultDiv = document.getElementById('result');
resultDiv.textContent = message;
resultDiv.className = `result ${type}`;
resultDiv.style.display = 'block';
}
// CSRFトークン取得
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
</script>
</body>
</html>