[Advent Calendar 2021][Day15]カレンダー参加機能を作る

はじめに

Advent Calendar 2021のDay15。 今回は、カレンダー参加機能を作っていきます。

graphqlクエリ・ミューテーションを定義する

下記を追記します。カレンダーのGetterと参加者のInsertを新規追加です。

  • gql/queries/calendar.graphlq
query GetCalendarByPkQuery($id: Int!) {
  calendars_by_pk(id: $id) {
    created_at
    id
    name
    participants {
      article_title
      article_url
      day
      updated_at
      created_at
      calendar_id
    }
    updated_at
  }
}

mutation InsertParticipantsMutation($object: participants_insert_input!) {
  insert_participants_one(object: $object) {
    updated_at
    uid
    day
    created_at
    calendar_id
    article_url
    article_title
  }
}

コードを生成します。

yarn graphql-codegen

新規ページを作成する

カレンダーの詳細ページを新規につくります。カレンダーIDでカレンダーを指定する。 UIは、次回作り込んでいくのでだいぶ適当です・・。

  • /calendars/[id].vue
<template>
    <div>
        <div v-for="(value, key) in calendarMap">
            <template v-if="value">
                <a :href="value.article_url">{{ value.article_title }}</a>
            </template>
            <template v-else>
                <button @click="openModal(key)">{{ `Day:${key}` }}</button>
            </template>
        </div>
        <div class="modal" :class="modalClass">
            <div class="modal-box">
                <p>参加</p>
                <div class="form-control">
                    <label class="label">
                        <span class="label-text">記事名</span>
                    </label>
                    <input
                        v-model="articleTitle"
                        type="text"
                        class="input input-primary input-bordered"
                    />
                    <label class="label">
                        <span class="label-text">記事URL</span>
                    </label>
                    <input
                        v-model="articleURL"
                        type="text"
                        class="input input-primary input-bordered"
                    />
                </div>
                <div class="modal-action">
                    <a @click="insert" class="btn btn-primary">参加する</a>
                    <a @click="closeModal" class="btn">閉じる</a>
                </div>
            </div>
        </div>
    </div>
</template>


<script setup lang="ts">
import { GetCalendarByPkQueryDocument, GetCalendarByPkQueryQueryVariables, InsertParticipantsMutationDocument, InsertParticipantsMutationMutationVariables } from "~/gql/queries/calendar";
import { useClientHandle } from "@urql/vue";
import { useNuxtApp, useAsyncData } from '#app'
import { useRoute, useRouter } from 'vue-router'
import { reactive, ref } from 'vue'
const nuxtApp = useNuxtApp()

const urql = useClientHandle();
const route = useRoute()
const variables: GetCalendarByPkQueryQueryVariables = {
    id: Number(route.params.id[0])
}
const queryResult = await urql.useQuery({ query: GetCalendarByPkQueryDocument, variables })
let { data, fetching } = await queryResult

let calendarMap = {}
for (let i = 1; i < 25; i++) {
    calendarMap[i] = null
}
for (const p of data.value.calendars_by_pk.participants) {
    calendarMap[p.day] = p
}

const insertCalendarResult = urql.useMutation(InsertParticipantsMutationDocument);
const insert = () => {
    const variables: InsertParticipantsMutationMutationVariables = {
        object: {
            article_title: articleTitle.value,
            article_url: articleURL.value,
            calendar_id: Number(route.params.id[0]),
            day: selectedDay.value
        }
    };
    insertCalendarResult.executeMutation(variables).then(async (result) => {
        await queryResult.executeQuery({
            requestPolicy: 'network-only',
        })
        closeModal()
    });
}
const articleTitle = ref('')
const articleURL = ref('')
const selectedDay = ref(0)
const modalClass = reactive<any>({ 'modal': true, 'modal-open': false })
const openModal = (day) => {
    modalClass['modal-open'] = true
    selectedDay.value = day
}
const closeModal = () => {
    modalClass['modal-open'] = false
}

</script>

トップページから新規のページに遷移できるようにします。

            <table v-if="data.calendars.length">
                <tbody>
                    <tr v-for="c in data.calendars" :key="c.id">
+                        <td>
+                            <NuxtLink :to="`/calendars/${c.id}`">{{ c.name }}</NuxtLink>
+                        </td>
                    </tr>
                </tbody>
            </table>

その他

topページの実装でuseStateを使っている所をreactive,refに修正。

まとめ

今回は、カレンダー参加機能を作りました。 次回は、カレンダー表示機能をつくります。