ページをリロードすると表記がおかしくなる
発生している問題・エラー
希望休をカレンダーに入力して、ページをリロードすると希望休を入力したカレンダー上の日付だけがずれてしまう(Firebase上では正しい日付になっている)。
該当するソースコード
ディレクトリ構造
root
┃
app
┣ login
┃ ┗ page.js
┣ mokuba
┃ ┣ admin
┃ ┃ ┗ requests
┃ ┃ ┃ ┗ page.js
┃ ┗ employee
┃ ┃ ┗ shift-input
┃ ┃ ┃ ┗ page.js
┣ providers
┃ ┗ AuthProvider.js
┣ signup
┃ ┗ page.js
┣ test
┃ ┣ admin
┃ ┃ ┗ requests
┃ ┃ ┃ ┗ page.js
┃ ┗ employee
┃ ┃ ┗ shift-input
┃ ┃ ┃ ┗ page.js
┣ favicon.ico
┣ globals.css
┣ layout.js
┣ page.js
┗ page.module.css
employee/shift-input/page.js
'use client';
import { useState, useEffect } from 'react';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import jaLocale from '@fullcalendar/core/locales/ja';
import { db } from '../../../../lib/firebase';
import {
addDoc,
collection,
getDocs,
doc,
updateDoc,
deleteDoc,
} from 'firebase/firestore';
export default function ShiftInputPage() {
const [events, setEvents] = useState([]);
const [selectedDate, setSelectedDate] = useState(null);
const [startTime, setStartTime] = useState('');
const [endTime, setEndTime] = useState('');
const [showModal, setShowModal] = useState(false);
const [editingEvent, setEditingEvent] = useState(null);
const [employeeName, setEmployeeName] = useState('');
const [isHolidayRequest, setIsHolidayRequest] = useState(false);
useEffect(() => {
const savedName = localStorage.getItem('employeeName');
if (savedName) setEmployeeName(savedName);
const fetchEvents = async () => {
const querySnapshot = await getDocs(collection(db, 'shiftRequests-test'));
const loadedEvents = querySnapshot.docs.map((doc) => {
const data = doc.data();
const date = data.date;
const start = new Date(`${date}T${data.startTime || '00:00'}`);
const end = data.endTime ? new Date(`${date}T${data.endTime}`) : null;
return {
id: doc.id,
title: data.isHolidayRequest ? '希望休' : data.title,
users: data.users,
start: start,
end: end,
isHolidayRequest: data.isHolidayRequest || false,
};
});
setEvents(loadedEvents);
};
fetchEvents();
}, []);
const handleDateClick = (arg) => {
setSelectedDate(arg.date);
setEditingEvent(null);
setStartTime('');
setEndTime('');
setIsHolidayRequest(false);
setShowModal(true);
};
const handleEditClick = (event) => {
const date = new Date(event.start);
setSelectedDate(date);
setStartTime(event.start.toTimeString().slice(0, 5));
setEndTime(event.end ? event.end.toTimeString().slice(0, 5) : ''); // ← 修正済み
setEditingEvent(event);
setIsHolidayRequest(event.isHolidayRequest || false);
setShowModal(true);
};
const handleSubmit = async () => {
if (!selectedDate || !employeeName) return;
const dateStr = selectedDate.toISOString().split('T')[0];
const title = isHolidayRequest ? '希望休' : `${startTime}〜${endTime}`;
const users = employeeName;
const start = new Date(selectedDate);
const end = new Date(selectedDate);
if (isHolidayRequest) {
start.setHours(9, 0, 0, 0);
end.setHours(10, 0, 0, 0);
} else {
const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);
start.setHours(startHour, startMinute, 0, 0);
end.setHours(endHour, endMinute, 0, 0);
}
const data = {
title,
date: dateStr,
startTime: isHolidayRequest ? '' : startTime,
endTime: isHolidayRequest ? '' : endTime,
users,
isHolidayRequest,
};
if (editingEvent) {
const eventDoc = doc(db, 'shiftRequests-test', editingEvent.id);
await updateDoc(eventDoc, data);
const updatedEvents = events.map((event) =>
event.id === editingEvent.id
? {
...event,
title,
start,
end: isHolidayRequest ? null : end,
users,
isHolidayRequest,
}
: event
);
setEvents(updatedEvents);
} else {
const docRef = await addDoc(collection(db, 'shiftRequests-test'), data);
setEvents([
...events,
{
id: docRef.id,
title,
start,
end: isHolidayRequest ? null : end,
users,
isHolidayRequest,
},
]);
}
setShowModal(false);
setStartTime('');
setEndTime('');
setSelectedDate(null);
setEditingEvent(null);
setIsHolidayRequest(false);
};
const handleDeleteEvent = async (id) => {
const eventDoc = doc(db, 'shiftRequests-test', id);
await deleteDoc(eventDoc);
setEvents((prev) => prev.filter((event) => event.id !== id));
setShowModal(false); // モーダルを閉じる
setEditingEvent(null); // 編集中のイベントをリセット
};
return (
<div>
<h1 className="text-xl font-bold mb-4">シフト希望入力画面(従業員)</h1>
<div className="mb-4">
<label className="text-lg">従業員名:</label>
<input
type="text"
value={employeeName}
onChange={(e) => {
setEmployeeName(e.target.value);
localStorage.setItem('employeeName', e.target.value);
}}
className="p-2 border rounded"
placeholder="従業員名を入力"
/>
</div>
<FullCalendar
key={events.length}
plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
initialView="dayGridMonth"
headerToolbar={{
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth',
}}
selectable={false}
editable={true}
events={events}
dateClick={handleDateClick}
locale={jaLocale}
timeZone="Asia/Tokyo"
height="auto"
eventContent={(arg) => {
const isHoliday = arg.event.extendedProps.isHolidayRequest;
return (
<div
className={`relative p-1 rounded ${
isHoliday ? 'bg-red-200' : 'bg-blue-100'
}`}
>
<div className="font-semibold text-sm break-words whitespace-normal">
{isHoliday
? '希望休'
: `${arg.event.start.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})}〜${
arg.event.end
? arg.event.end.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})
: ''
}`}
</div>
<div className="text-xs text-gray-600">
{arg.event.extendedProps.users || '従業員名未設定'}
</div>
<button
onClick={(e) => {
e.stopPropagation();
const targetEvent = events.find((evt) => evt.id === arg.event.id);
handleEditClick(targetEvent);
}}
className="text-xs text-blue-600 underline mt-1"
>
編集
</button>
</div>
);
}}
/>
{showModal && (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-lg p-8 w-full max-w-md">
<h2 className="text-xl font-bold mb-4 text-center">
{editingEvent ? 'シフト編集' : 'シフト希望入力'}
</h2>
<p className="mb-4 text-center text-lg">
選択日: {selectedDate?.toLocaleDateString()}
</p>
<div className="flex flex-col gap-4 text-lg">
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={isHolidayRequest}
onChange={(e) => setIsHolidayRequest(e.target.checked)}
/>
希望休(この日は勤務できません)
</label>
{!isHolidayRequest && (
<>
<label className="flex flex-col">
開始時刻:
<input
type="time"
value={startTime}
onChange={(e) => setStartTime(e.target.value)}
className="mt-1 p-2 border rounded"
/>
</label>
<label className="flex flex-col">
終了時刻:
<input
type="time"
value={endTime}
onChange={(e) => setEndTime(e.target.value)}
className="mt-1 p-2 border rounded"
/>
</label>
</>
)}
</div>
<div className="mt-6 flex justify-end gap-2">
<button
onClick={() => {
setShowModal(false);
setEditingEvent(null);
setIsHolidayRequest(false);
}}
className="px-4 py-2 bg-gray-400 text-white rounded hover:bg-gray-500"
>
キャンセル
</button>
{editingEvent && (
<button
onClick={() => handleDeleteEvent(editingEvent.id)}
className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
>
削除
</button>
)}
<button
onClick={handleSubmit}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
保存
</button>
</div>
</div>
</div>
)}
</div>
);
}
providers/AuthProvider.js
'use client';
import { onAuthStateChanged } from 'firebase/auth';
import { doc, getDoc } from 'firebase/firestore';
import { createContext, useContext, useEffect, useState } from 'react';
import { auth, db } from '../../lib/firebase';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [role, setRole] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (firebaseUser) => {
if (firebaseUser) {
const docRef = doc(db, 'users', firebaseUser.uid);
const docSnap = await getDoc(docRef);
setUser(firebaseUser);
setRole(docSnap.data()?.role);
} else {
setUser(null);
setRole(null);
}
setLoading(false);
});
return () => unsubscribe();
}, []);
return (
<AuthContext.Provider value={{ user, role, loading }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
自分で試したこと
firebaseに正しくデータが送れているかの確認。以下のようになっており問題はないはずです。
date
"2025-05-07"
endTime
""
isHolidayRequest
true
startTime
""
title
"希望休"
users
"test1"
date
"2025-05-09"
endTime
""
isHolidayRequest
true
startTime
""
title
"希望休"
users
"test1"
0 likes