内容
休業日と営業日を切り替えて表示するために、以下のコードを実装しました。
備忘録なので説明は省いています(面倒くさいので・・・)
app/controllers/reservations_controller.rb
def update_slots
date = params[:date]
@date = Date.parse(date)
@company = current_company
@time_slots = generate_time_slots(@company)
@non_business_day = ScheduleService.is_non_business_day?(@company, @date)
@services = @company.services
@service_availability = ScheduleService.calculate_availability(@company, @date, @services, @time_slots)
response_data = {
services: @services.map { |service|
{
name: service.name,
duration: format_duration(service.duration),
time_slots: @time_slots.map { |slot| { availability: @service_availability.dig(service.id, slot) || '-' } },
}
},
time_slots: @time_slots,
non_business_day: @non_business_day
}
render json: response_data
end
ビュー (app/views/schedules/show.html.erb
)
<div class="schedule-container">
<% if @non_business_day %>
<h2><%= "#{@date.strftime("%Y年%m月%d日")}は休業日です" %></h2>
<% else %>
<div>
○: 空き枠あり、△: 空き枠わずか、- : 要確認、×: 受付不可
</div>
<div class="table-responsive">
<table class="table table-bordered text-center">
<thead class="thead-light">
<tr>
<th class="sticky-left">サービス</th>
<th class="sticky-duration">所要時間</th>
<% @time_slots.each do |slot| %>
<th><%= slot %></th>
<% end %>
</tr>
</thead>
<tbody>
<% @services.each do |service| %>
<tr>
<th class="service-name sticky-left"><%= service.name %></th>
<th class="service-duration sticky-duration"><%= format_duration(service.duration) %></th>
<% @time_slots.each do |slot| %>
<td>
<%= @service_availability[service.id][slot] %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
<%= paginate @services %>
<% end %>
</div>
JavaScript (app/javascript/packs/application.js
)
// ある日のスケジュールとかを取得するときの非同期通信用
document.addEventListener('turbo:load', function() {
const dateField = document.querySelector('#for_customer_index_date_field');
if (dateField) {
const companyId = dateField.dataset.companyId;
const scheduleTitle = document.querySelector('.page-index');
let companyName = '';
if (scheduleTitle && scheduleTitle.textContent) {
const match = scheduleTitle.textContent.match(/^(.*?)の/);
if (match) {
companyName = match[1];
}
}
const debouncedFetch = debounce(function(date) {
fetch(`/reservations/update_slots?date=${date}&company_id=${companyId}`, {
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.querySelector("[name='csrf-token']").content
},
credentials: 'include'
})
.then(response => response.json())
.then(data => {
if (data && data.services && data.time_slots !== undefined) {
updateTable({ ...data, date });
if (companyName) {
scheduleTitle.textContent = `${formatDate(date)}のスケジュール`; // 安全に更新
}
} else {
console.error("Invalid data format received:", data);
}
})
.catch(error => console.error('Error fetching data:', error));
}, 500);
dateField.addEventListener('input', function() {
debouncedFetch(this.value);
});
}
});
function updateTable(data) {
const scheduleContainer = document.querySelector('.schedule-container');
if (data.non_business_day) {
// 休業日の場合、全ての内容を削除して休業日メッセージを表示
scheduleContainer.innerHTML = `<h2>${formatDate(data.date)}は休業日です</h2>`;
} else {
// 営業日の場合の表示
scheduleContainer.innerHTML = `
<div>
○: 空き枠あり、△: 空き枠わずか、- : 要確認、×: 受付不可
</div>
<div class="table-responsive">
<table class="table table-bordered text-center">
<thead class="thead-light">
<tr>
<th class="sticky-left">サービス</th>
<th class="sticky-duration">所要時間</th>
${data.time_slots.map(slot => `<th>${slot}</th>`).join('')}
</tr>
</thead>
<tbody>
${data.services.map(service => `
<tr>
<th class="service-name sticky-left">${service.name}</th>
<th class="service-duration sticky-duration">${service.duration}</th>
${service.time_slots.map(slot => `<td>${slot.availability}</td>`).join('')}
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
}
// 年月日のフォーマット関数
function formatDate(date) {
const d = new Date(date);
const year = d.getFullYear();
const month = ('0' + (d.getMonth() + 1)).slice(-2); // 月を2桁にフォーマット
const day = ('0' + d.getDate()).slice(-2); // 日を2桁にフォーマット
return `${year}年${month}月${day}日`;
}
// デバウンス関数
function debounce(func, wait) {
let timeout;
return function() {
const context = this, args = arguments;
const later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}