<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ライブイベント チケット希望枚数フォーム</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f3f4f6; /* Light gray background */
}
.container {
max-width: 800px;
margin: 2rem auto;
padding: 2rem;
background-color: #ffffff;
border-radius: 0.75rem; /* Rounded corners */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.form-group label {
font-weight: 500;
color: #374151; /* Darker gray for labels */
margin-bottom: 0.5rem;
display: block;
}
.form-group input[type="text"],
.form-group input[type="number"] {
width: 100%;
padding: 0.75rem;
border: 1px solid #d1d5db; /* Light gray border */
border-radius: 0.5rem; /* Rounded corners */
outline: none;
transition: border-color 0.2s;
}
.form-group input[type="text"]:focus,
.form-group input[type="number"]:focus {
border-color: #6366f1; /* Indigo focus color */
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}
.form-group input[type="checkbox"] {
transform: scale(1.2); /* Slightly larger checkbox */
margin-right: 0.5rem;
}
.btn-submit {
background-color: #6366f1; /* Indigo button */
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
display: block;
width: 100%;
text-align: center;
}
.btn-submit:hover {
background-color: #4f46e5; /* Darker indigo on hover */
box-shadow: 0 4px 6px rgba(99, 102, 241, 0.2);
}
.summary-table th, .summary-table td {
padding: 0.75rem;
border-bottom: 1px solid #e5e7eb; /* Light gray border for table rows */
text-align: left;
}
.summary-table th {
background-color: #f9fafb; /* Lighter background for table header */
font-weight: 600;
color: #4b5563; /* Darker gray for table header text */
}
</style>
</head>
<body>
<div class="container">
<h1 class="text-3xl font-bold text-gray-800 mb-6 text-center">ライブイベント チケット希望枚数フォーム</h1>
<p class="text-gray-600 mb-8 text-center">各出演者にはお客様用にチケット1枚が割り当てられています。出演者ご自身のチケットは不要です。</p>
<form id="ticketForm" class="space-y-6">
<div class="form-group">
<label for="performerName">出演者名:</label>
<input type="text" id="performerName" name="performerName" placeholder="例: バンドA" required class="mt-1">
</div>
<div class="form-group">
<label for="additionalDesiredTickets">希望追加枚数 (割り当てられたお客様用チケット1枚に加えて、追加で必要なお客様用チケット枚数):</label>
<input type="number" id="additionalDesiredTickets" name="additionalDesiredTickets" min="0" value="0" required class="mt-1">
</div>
<div class="form-group">
<label for="ticketsReceivedFromOthers">他の出演者から譲り受ける枚数:</label>
<input type="number" id="ticketsReceivedFromOthers" name="ticketsReceivedFromOthers" min="0" value="0" class="mt-1">
</div>
<div class="form-group flex items-center">
<input type="checkbox" id="releasesAllocatedCustomerTicket" name="releasesAllocatedCustomerTicket" class="rounded-md text-indigo-600 focus:ring-indigo-500">
<label for="releasesAllocatedCustomerTicket" class="ml-2 mb-0">割り当てられたお客様用チケット1枚を不要とする (余剰として出す): <span class="text-gray-500 text-sm">(このチケットを必要としない場合はチェックしてください。)</span></label>
</div>
<button type="submit" class="btn-submit">送信</button>
</form>
<div id="messageBox" class="hidden mt-6 p-4 rounded-md text-sm" role="alert"></div>
<div class="mt-10">
<h2 class="text-2xl font-bold text-gray-800 mb-4 text-center">集計結果</h2>
<div class="overflow-x-auto">
<table class="min-w-full bg-white rounded-md summary-table">
<thead>
<tr>
<th class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider rounded-tl-md">出演者名</th>
<th class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">希望追加枚数</th>
<th class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">譲り受け枚数</th>
<th class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider">割り当てチケット放出</th>
<th class="px-6 py-3 border-b-2 border-gray-200 text-left text-xs font-semibold text-gray-600 uppercase tracking-wider rounded-tr-md">純チケット要求/余剰</th>
</tr>
</thead>
<tbody id="summaryTableBody" class="divide-y divide-gray-200">
<!-- Data will be inserted here by JavaScript -->
</tbody>
</table>
</div>
<div id="overallSummary" class="mt-6 p-4 bg-indigo-50 border border-indigo-200 text-indigo-800 rounded-md text-lg font-semibold text-center">
全体の純チケット要求数: 0枚
</div>
</div>
</div>
<script>
// Array to store submitted performer data
let performersData = [];
// Constants for initial allocation
const INITIAL_ALLOCATION_PER_PERFORMER = 1; // 各出演者に割り当てられたお客様用チケット枚数
// Get elements
const ticketForm = document.getElementById('ticketForm');
const summaryTableBody = document.getElementById('summaryTableBody');
const overallSummary = document.getElementById('overallSummary');
const messageBox = document.getElementById('messageBox');
// Function to display message box
function showMessage(message, type = 'info') {
messageBox.textContent = message;
messageBox.classList.remove('hidden', 'bg-red-100', 'border-red-400', 'text-red-700', 'bg-green-100', 'border-green-400', 'text-green-700', 'bg-blue-100', 'border-blue-400', 'text-blue-700');
if (type === 'error') {
messageBox.classList.add('bg-red-100', 'border-red-400', 'text-red-700');
} else if (type === 'success') {
messageBox.classList.add('bg-green-100', 'border-green-400', 'text-green-700');
} else {
messageBox.classList.add('bg-blue-100', 'border-blue-400', 'text-blue-700');
}
messageBox.classList.remove('hidden');
setTimeout(() => {
messageBox.classList.add('hidden');
}, 3000); // Hide after 3 seconds
}
// Function to render the summary table and overall summary
function renderSummary() {
summaryTableBody.innerHTML = ''; // Clear existing table rows
let totalNetTicketsRequired = 0;
performersData.forEach(performer => {
let ticketsForCustomerFromPool = performer.additionalDesiredTickets;
// 割り当てられたお客様用チケットを放出する場合、その分プールへの要求が減る
if (performer.releasesAllocatedCustomerTicket) {
ticketsForCustomerFromPool -= INITIAL_ALLOCATION_PER_PERFORMER;
}
// 他の出演者から譲り受けたチケットはプールからの要求を減らす
const netTicketsForPerformer = ticketsForCustomerFromPool - performer.ticketsReceivedFromOthers;
totalNetTicketsRequired += netTicketsForPerformer;
// Create a new table row for the performer
const row = document.createElement('tr');
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">${performer.performerName}</td>
<td class="px-6 py-4 whitespace-nowrap">${performer.additionalDesiredTickets}</td>
<td class="px-6 py-4 whitespace-nowrap">${performer.ticketsReceivedFromOthers}</td>
<td class="px-6 py-4 whitespace-nowrap">${performer.releasesAllocatedCustomerTicket ? 'はい' : 'いいえ'}</td>
<td class="px-6 py-4 whitespace-nowrap">
${netTicketsForPerformer > 0 ? `<span class="text-red-600">${netTicketsForPerformer}枚 (追加要求)</span>` :
netTicketsForPerformer < 0 ? `<span class="text-green-600">${Math.abs(netTicketsForPerformer)}枚 (余剰提供)</span>` :
`0枚 (相殺済み)`}
</td>
`;
summaryTableBody.appendChild(row);
});
// Update overall summary
overallSummary.innerHTML = `
全体の純チケット要求数:
${totalNetTicketsRequired > 0 ? `<span class="text-red-800">${totalNetTicketsRequired}枚 (追加要求)</span>` :
totalNetTicketsRequired < 0 ? `<span class="text-green-800">${Math.abs(totalNetTicketsRequired)}枚 (全体余剰)</span>` :
`0枚 (全体相殺済み)`}
`;
}
// Handle form submission
ticketForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const performerName = document.getElementById('performerName').value.trim();
const additionalDesiredTickets = parseInt(document.getElementById('additionalDesiredTickets').value, 10);
const ticketsReceivedFromOthers = parseInt(document.getElementById('ticketsReceivedFromOthers').value, 10);
const releasesAllocatedCustomerTicket = document.getElementById('releasesAllocatedCustomerTicket').checked;
// Basic validation
if (!performerName) {
showMessage('出演者名を入力してください。', 'error');
return;
}
if (isNaN(additionalDesiredTickets) || additionalDesiredTickets < 0) {
showMessage('希望追加枚数は0以上の数字で入力してください。', 'error');
return;
}
if (isNaN(ticketsReceivedFromOthers) || ticketsReceivedFromOthers < 0) {
showMessage('他の出演者から譲り受ける枚数は0以上の数字で入力してください。', 'error');
return;
}
// Create a performer object
const newPerformer = {
performerName,
additionalDesiredTickets,
ticketsReceivedFromOthers,
releasesAllocatedCustomerTicket
};
// Add to the data array
performersData.push(newPerformer);
// Render the updated summary
renderSummary();
// Clear the form fields
ticketForm.reset();
showMessage('データが正常に送信されました!', 'success');
});
// Initial render of summary (empty table)
renderSummary();
</script>
</body>
</html>