[Advent Calendar 2021][Day23]カレンダーの更新削除を実装する(3)

はじめに

Advent Calendar 2021のDay23。 カレンダー更新を実装していきます。

UI Componentを分割する

カレンダー詳細画面でカレンダーの更新と削除を行えるようにドロップダウンメニューを作成します。 トップページとカレンダー詳細で同じモーダルを使えそうなので、共通化しようと思います。

  • components/Dropdown.vue
<template>
    <div class="flex-none">
        <div class="dropdown dropdown-end">
            <div tabindex="0" class="m-1">
                <button class="btn btn-ghost btn-square" aria-label="button component">
                    <svg
                        xmlns="http://www.w3.org/2000/svg"
                        fill="none"
                        viewBox="0 0 24 24"
                        class="inline-block w-6 h-6 stroke-current"
                    >
                        <path
                            stroke-linecap="round"
                            stroke-linejoin="round"
                            stroke-width="2"
                            d="M5 12h.01M12 12h.01M19 12h.01M6 12a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z"
                        />
                    </svg>
                </button>
            </div>
            <ul tabindex="0" class="p-2 shadow menu dropdown-content bg-base-100 rounded-box w-52">
                <slot />
            </ul>
        </div>
    </div>
</template>
  • components/CalendarModal.vue
<template>
    <Modal :title="title" :open="open">
        <div class="form-control">
            <label class="label">
                <span class="label-text">カレンダー名</span>
            </label>
            <input
                v-model="calendarName"
                type="text"
                class="input input-primary input-bordered"
                @input="$emit('update:calendarName', $event.target.value)"
            />
        </div>

        <div v-if="errorMessage" class="mt-4 alert alert-error">
            <div class="flex-1">
                <svg
                    xmlns="http://www.w3.org/2000/svg"
                    fill="none"
                    viewBox="0 0 24 24"
                    class="w-6 h-6 mx-2 stroke-current"
                >
                    <path
                        stroke-linecap="round"
                        stroke-linejoin="round"
                        stroke-width="2"
                        d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"
                    />
                </svg>
                <label>{{ errorMessage }}</label>
            </div>
        </div>
    </Modal>
</template>
<script>
export default {
    data() {
        return {
            calendarName: "",
        }
    },
    props: {
        title: {
            type: String,
            default: 'カレンダー作成'
        },
        open: {
            type: Boolean,
            default: false
        },
        calendarName: {
            type: String,
            default: ''
        },
        errorMessage: {
            type: String,
            default: ''
        }
    },
}
</script>

graphql

参加者とカレンダーのクエリが一緒になっていたので分割します。更新にはActionを使うよう修正します。

  • gql/queries/calendar.graphql
query ListCalendarsQuery($limit: Int = 10, $created_at: order_by = desc) {
  calendars(limit: $limit, order_by: {created_at: $created_at}) {
    id
    name
    updated_at
    created_at
  }
}

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 ActionCreateCalendarMutation($name: String = "") {
  actionCreateCalendar(in: {name: $name}) {
    created_at
    id
    name
    uid
    updated_at
  }
}

mutation ActionUpdateCalendarMutation($id: Int!, $name: String!) {
  actionUpdateCalendar(in: {id: $id, name: $name}) {
    id
    name
    uid
    created_at
    updated_at
  }
}

  • gql/queries/participants.graphql
mutation InsertParticipantsMutation($object: participants_insert_input!) {
  insert_participants_one(object: $object) {
    updated_at
    uid
    day
    created_at
    calendar_id
    article_url
    article_title
  }
}

ページのテンプレート部分をドロップダウンメニューとカレンダーモーダルを使うよう修正。

  • pages/calendars/[id].vue
<template>
    <div>
        <div class="flex">
            <div class="flex-auto">
                <h1 class="text-2xl font-bold">{{ calendarTitle }}</h1>
            </div>
            <div v-if="!!user" class="flex-none">
                <Dropdown>
                    <li class="menu-title">
                        <span>カレンダー</span>
                    </li>
                    <li>
                        <a @click="openCalendarModal = true">編集</a>
                    </li>
                    <li>
                        <a>削除</a>
                    </li>
                </Dropdown>
                <CalendarModal
                    title="カレンダー編集"
                    :open="openCalendarModal"
                    @ok="onCalendarOkClick"
                    @cancel="openCalendarModal = false"
                    v-model:calendarName="calendarName"
                    :errorMessage="errorMessage"
                />
            </div>
        </div>
        <div class="grid grid-cols-1 md:gap-4 md:grid-cols-6">
            <div v-for="(value, key) in calendarMap" class="card shadow">
                <div class="card-body">
                    <a v-if="value" :href="value.article_url" target="_blank">
                        <h2
                            class="card-title link"
                        >{{ `Day${key}: ${value.article_title || 'タイトルなし'}` }}</h2>
                    </a>
                    <a v-else-if="user" @click="selectedDay = key; openModal = true">
                        <h2 class="card-title link">{{ `Day:${key}` }}</h2>
                    </a>
                    <span v-else>
                        <h2 class="card-title">{{ `Day:${key}` }}</h2>
                    </span>
                </div>
            </div>
        </div>
        <Modal :title="modalTitle" :open="openModal" @ok="onOkClick" @cancel="openModal = false">
            <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>
        </Modal>
    </div>
</template>

その他

不具合の修正。

まとめ

今回はカレンダーの更新機能を実装しました。 次回は、カレンダーの削除機能を実装していきます。