Uncategorized

カレンダー作成

完了したタスクをカレンダーに記録するため
週ごとのカレンダーを作成したい

そのためvueを使用して作っていく

ステップ1:カレンダーの基本的な表示

まずはカレンダーの基本的な表示を作成しましょう。画像のようなシンプルな月表示を目指します。

  1. Vue.jsプロジェクトの準備: もし既存のVue.jsプロジェクトがない場合は、以下のコマンドで新しいプロジェクトを作成します。 Bashnpm install -g @vue/cli vue create calendar-app プロジェクト作成時にいくつかの質問が出ますが、Vue 3を選択しておけば問題ありません。
  2. カレンダーコンポーネントの作成: src/components ディレクトリに Calendar.vue という新しいファイルを作成します。
  3. Calendar.vue の記述: 以下は基本的なカレンダー表示のテンプレートと簡単なロジックです。まずはこれを Calendar.vue に記述してみてください。
<template>
  <div>
    <h2>{{ year }}年 {{ month }}月</h2>
    <table>
      <thead>
        <tr>
          <th v-for="day in daysOfWeek" :key="day">{{ day }}</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(week, index) in calendarGrid" :key="index">
          <td v-for="(date, i) in week" :key="i" :class="{ 'today': isToday(date), 'empty': !date }">
            {{ date ? date.getDate() : '' }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  name: 'PomodoroCalendar', 
  data() {
    const today = new Date();
    const currentYear = today.getFullYear();
    const currentMonth = today.getMonth() + 1; // JavaScriptのgetMonth()は0から始まるため+1
    return {
      year: currentYear,
      month: currentMonth,
      daysOfWeek: ['日', '月', '火', '水', '木', '金', '土'],
      todayDate: today.getDate(),
      todayMonth: currentMonth,
      todayYear: currentYear,
    };
  },
  computed: {
    daysInMonth() {
      return new Date(this.year, this.month, 0).getDate();
    },
    firstDayOfMonth() {
      return new Date(this.year, this.month - 1, 1).getDay(); // 0:日, 1:月,...
    },
    calendarGrid() {
      const grid = [];
      let week = [];
      let dayCounter = 1;

      // 月の初めまでの空白を追加
      for (let i = 0; i < this.firstDayOfMonth; i++) {
        week.push(null);
      }

      while (dayCounter <= this.daysInMonth) {
        week.push(new Date(this.year, this.month - 1, dayCounter));
        if (week.length === 7) {
          grid.push(week);
          week = [];
        }
        dayCounter++;
      }

      // 月の終わりまでの空白を追加
      while (week.length < 7 && week.length > 0) {
        week.push(null);
      }
      if (week.length > 0) {
        grid.push(week);
      }

      return grid;
    },
  },
  methods: {
    isToday(date) {
      return date && date.getDate() === this.todayDate && date.getMonth() + 1 === this.todayMonth && date.getFullYear() === this.todayYear;
    },
  },
};
</script>

<style scoped>
table {
  width: 100%;
  border-collapse: collapse;
}
th, td {
  border: 1px solid #ccc;
  padding: 8px;
  text-align: center;
}
.today {
  background-color: lightblue;
}
.empty {
  background-color: #eee;
}
</style>
  1. App.vueCalendar コンポーネントを使用: src/App.vue を以下のように修正して、作成した Calendar コンポーネントを表示します。
<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <PomodoroCalendar />
</template>

<script>
import PomodoroCalendar from './components/PomodoroCalendar.vue';

export default {
  name: 'App',
  components: {
    PomodoroCalendar
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

ステップ2:カレンダーに作業記録を表示する準備

次に、カレンダーの各日付に作業記録を表示するための準備をします。

  1. PomodoroCalendar.vue に作業記録データを保持する: PomodoroCalendar.vuedata オプションに、この作業記録データを初期値として設定します
export default {
  data() {
    // ... 既存のデータ
    return {
      // ... 既存のデータ
      workLogs: [], // 作業記録を保存する配列
    };
  },
  // ... 既存の computed と methods
};
  1. カレンダーの日付に紐付けるメソッドの準備: カレンダーの各セルに作業記録を表示するために、日付に対応する作業記録を抽出するメソッドを Calendar.vue に追加します。
export default {
  // ... data, computed
  methods: {
    // ... 既存のメソッド
    getWorkLogsForDate(date) {
      if (!date) {
        return [];
      }
      const formattedDate = `<span class="math-inline">\{date\.getFullYear\(\)\}\-</span>{(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
      return this.workLogs.filter(log => log.date === formattedDate);
    },
  },
};
  1. テンプレートの修正: カレンダーの <td> 要素内で getWorkLogsForDate メソッドを呼び出し、該当する作業記録を表示するようにテンプレートを修正します。簡単な表示として、タスク名を表示してみましょう。 この時点では workLogs は空なので、まだ何も表示されません。
<template>
  <div>
    <h2>{{ year }}年 {{ month }}月</h2>
    <table>
      <thead>
        <tr>
          <th v-for="day in daysOfWeek" :key="day">{{ day }}</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(week, index) in calendarGrid" :key="index">
          <td
            v-for="(date, i) in week"
            :key="i"
            :class="{ 'today': isToday(date), 'empty': !date }"
          >
            {{ date ? date.getDate() : '' }}
            <div v-if="date">
              <div v-for="log in getWorkLogsForDate(date)" :key="log.startTime">
                - {{ log.task }} ({{ log.startTime }} - {{ log.endTime }})
              </div>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

カレンダーとタイマーの紐付け

上記のコードをPomodoroTimerプロジェクトのcomponentsに追加して、紐付けを行う

まず、PomodoroCalendar.vue<script> タグ内の recordWorkTime メソッドを修正し、ポモドーロタイマーから渡されるイベントデータを適切に処理するようにします。

PomodoroCalendar.vue<script>methods 部分の修正:

methods: {
  isToday(date) {
    return (
      date &&
      date.getDate() === this.todayDate &&
      date.getMonth() + 1 === this.todayMonth &&
      date.getFullYear() === this.todayYear
    );
  },
  getWorkLogsForDate(date) {
    if (!date) {
      return [];
    }
    const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1)
      .toString()
      .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
    return this.workLogs.filter((log) => log.date === formattedDate);
  },
  recordWorkTime(logData) {
    const logEntry = {
      date: logData.date,
      task: logData.task || '集中作業', // タスク名がない場合はデフォルト値を設定
      startTime: logData.startTime,
      endTime: logData.endTime,
    };
    this.workLogs.push(logEntry);
    console.log("PomodoroCalendar で作業ログを記録:", logEntry);
  },
},

解説:

  • recordWorkTime メソッドは、ポモドーロタイマーから $emit される work-finished イベントで渡される logData オブジェクトを受け取るように変更しました。
  • logDatatask プロパティが含まれていない場合に備えて、デフォルト値として '集中作業' を設定するようにしました。
  • 日付の形式は、ポモドーロタイマーから送信される形式 (YYYY-MM-DD) をそのまま使用します。

1. startWorkTimerInternal() メソッド内の作業開始時刻の記録:

JavaScript

  startWorkTimerInternal() {
    this.isWorkTimerActive = true;
    this.isWorkTimerPaused = false;
    this.currentTimerLabel = "作業時間";
    this.remainingWorkSeconds = this.minutes * 60 + this.seconds;
    this.workStartTime = new Date().toLocaleTimeString([], {
      hour: "2-digit",
      minute: "2-digit",
    }); // 作業開始時刻を記録
    // ... (タイマーのインターバル処理)
  },
  • this.workStartTime = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });:
    • new Date(): 現在の日時オブジェクトを作成します。
    • .toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }): 現在の時刻を、時と分を2桁で表示する形式の文字列に変換します(例: "13:09")。
    • この開始時刻の文字列をコンポーネントの data プロパティ workStartTime に保存します。

2. startWorkTimerInternal() メソッド内の作業終了時のイベント発行:

JavaScript

      // ... (タイマーのインターバル処理)
      } else {
        this.stopTimerInterval();
        this.isWorkTimerActive = false;
        this.isWorkTimerPaused = false;
        this.sendTimerEndStatus("work");
        const workEndTime = new Date().toLocaleTimeString([], {
          hour: "2-digit",
          minute: "2-digit",
        });
        this.$emit("work-finished", {
          date: new Date().toISOString().slice(0, 10),
          startTime: this.workStartTime,
          endTime: workEndTime,
          task: "集中作業", // ここは必要に応じて変更可能
        });
        alert("作業時間終了!休憩を開始してください。");
        this.breakTimerStartButton = true;
      }
      // ... (タイマーのインターバル処理の続き)
  • const workEndTime = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });:
    • 作業時間が終了した時点の時刻を、開始時刻と同様の形式で取得し、workEndTime 変数に保存します。
  • this.$emit("work-finished", { ... });:
    • this.$emit(): このコンポーネントから親コンポーネント(ここでは PomodoroCalendar)へイベントを発行します。
    • "work-finished": 発行するイベントの名前です。親コンポーネントはこの名前のイベントをリッスンします。
    • { ... }: イベントと共に送信するデータオブジェクトです。
      • date: new Date().toISOString().slice(0, 10): 現在の日付を YYYY-MM-DD 形式の文字列で送信します。
      • startTime: this.workStartTime: 作業開始時に記録した時刻を送信します。
      • endTime: workEndTime: 作業終了時に取得した時刻を送信します。
      • task: "集中作業": 現在は固定で "集中作業" というタスク名を送信しています。必要に応じて、ユーザーがタスクを入力できるようなUIを追加し、その入力値を送信するように変更できます。

3. resumeTimer("work") メソッド内の開始時刻の記録(再開時):

JavaScript

    resumeTimer(timerType) {
      if (!this.timerInterval) {
        if (
          timerType === "work" &&
          this.isWorkTimerPaused &&
          this.isWorkTimerActive
        ) {
          this.isWorkTimerPaused = false;
          this.workStartTime =
            this.workStartTime ||
            new Date().toLocaleTimeString([], {
              hour: "2-digit",
              minute: "2-digit",
            }); // 再開時に開始時刻が未設定なら記録
          // ... (タイマーのインターバル処理)
        }
        // ... (休憩タイマーの再開処理)
      }
    },
  • this.workStartTime = this.workStartTime || new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });:
    • タイマーが一時停止から再開されたときに、this.workStartTime がまだ設定されていない(例えば、開始直後に一時停止した場合など)場合に、現在の時刻を開始時刻として記録します。
    • || は「左側の値が falsy (null, undefined, 0, false, '') ならば右側の値を評価する」というJavaScriptの論理OR演算子を利用したイディオムです。

これらの変更により、ポモドーロタイマーが作業時間の開始と終了を正確に把握し、終了時にその情報を work-finished イベントを通じて親コンポーネントである PomodoroCalendar に送信できるようになりました。PomodoroCalendar はこのイベントを受け取り、作業ログを記録してカレンダーに表示します。

上記の変更をしたことでカレンダーに作業時間が追加されました!

-Uncategorized