From 571176016f39316638f3ea74734c9aec8099e210 Mon Sep 17 00:00:00 2001
From: JunGu Kang <chr0m3.kr@gmail.com>
Date: Tue, 10 Dec 2019 01:16:46 +0900
Subject: [PATCH] #21 Add Schedule Detail View

---
 src/router/index.js          |   5 +
 src/views/ScheduleDetail.vue | 293 +++++++++++++++++++++++++++++++++++
 2 files changed, 298 insertions(+)
 create mode 100644 src/views/ScheduleDetail.vue

diff --git a/src/router/index.js b/src/router/index.js
index 4285289..752f888 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -43,6 +43,11 @@ const routes = [
     name: 'ScheduleCreate',
     component: () => import('../views/ScheduleCreate.vue'),
   },
+  {
+    path: '/schedule/:id',
+    name: 'ScheduleDetail',
+    component: () => import('@/views/ScheduleDetail.vue'),
+  },
   {
     path: '/trainee',
     name: 'TraineeList',
diff --git a/src/views/ScheduleDetail.vue b/src/views/ScheduleDetail.vue
new file mode 100644
index 0000000..d3ff6e9
--- /dev/null
+++ b/src/views/ScheduleDetail.vue
@@ -0,0 +1,293 @@
+<template>
+  <div class="schedule-detail">
+    <v-row>
+      <v-col
+        cols="12"
+      >
+        <v-card
+          :loading="isProcessing"
+        >
+          <v-card-title>일정 정보</v-card-title>
+          <v-card-text>
+            <v-alert
+              v-if="error.isError"
+              dense
+              text
+              type="error"
+            >
+              {{ error.message }}
+            </v-alert>
+            <v-text-field
+              label="등록번호"
+              readonly
+              v-model="id"
+            ></v-text-field>
+            <program-field
+              :program.sync="schedule.program"
+            ></program-field>
+            <trainer-field
+              :trainer.sync="schedule.trainer"
+            ></trainer-field>
+            <v-text-field
+              label="최대 예약 가능 인원"
+              v-model="schedule.maxTrainee"
+            ></v-text-field>
+            <date-time-field
+              :datetime.sync="schedule.startAt"
+            ></date-time-field>
+            <v-text-field
+              label="등록 시각"
+              readonly
+              v-model="schedule.createdAt"
+            ></v-text-field>
+            <v-text-field
+              label="최종 수정 시각"
+              readonly
+              v-model="schedule.updatedAt"
+            ></v-text-field>
+          </v-card-text>
+          <v-card-actions>
+            <v-spacer></v-spacer>
+            <v-btn
+              color="error"
+              outlined
+              @click="deleteSchedule(id)"
+            >
+              <v-icon
+                left
+              >
+                mdi-delete
+              </v-icon>
+              삭제
+            </v-btn>
+            <v-btn
+              color="primary"
+              outlined
+              @click="updateSchedule(id)"
+            >
+              <v-icon
+                left
+              >
+                mdi-content-save
+              </v-icon>
+              저장
+            </v-btn>
+          </v-card-actions>
+        </v-card>
+      </v-col>
+    </v-row>
+    <v-row>
+      <v-col
+        cols="12"
+      >
+        <v-card
+          :loading="isProcessing"
+        >
+          <v-card-title>예약자</v-card-title>
+          <v-card-text>
+            <v-row
+              justify="end"
+            >
+              <v-col
+                cols="12"
+                sm="4"
+                md="3"
+                lg="3"
+                xl="2"
+              >
+                <v-text-field
+                  append-icon="mdi-magnify"
+                  clearable
+                  label="회원 검색"
+                  v-model="traineeList.searchKeyword"
+                ></v-text-field>
+              </v-col>
+            </v-row>
+            <v-data-table
+              :headers="traineeList.headers"
+              item-key="id"
+              :items="schedule.trainees"
+              :loading="isProcessing"
+              loading-text="데이터를 불러오는 중입니다."
+              :search="traineeList.searchKeyword"
+              no-results-text="일치하는 회원을 찾지 못했습니다."
+            >
+              <template v-slot:item.action="{ item }">
+                <v-icon
+                  small
+                  class="mr-2"
+                  @click="editTrainee(item.id)"
+                >
+                  mdi-pencil
+                </v-icon>
+                <v-icon
+                  small
+                  @click="deleteTrainee(item.id)"
+                >
+                  mdi-delete
+                </v-icon>
+              </template>
+            </v-data-table>
+          </v-card-text>
+        </v-card>
+      </v-col>
+    </v-row>
+  </div>
+</template>
+
+<script>
+import APISetting from '@/settings/api';
+import DateTimeField from '@/components/DateTimeField.vue';
+import ProgramField from '@/components/ProgramField.vue';
+import TrainerField from '@/components/TrainerField.vue';
+
+export default {
+  name: 'ScheduleDetail',
+
+  components: {
+    DateTimeField,
+    ProgramField,
+    TrainerField,
+  },
+
+  data: () => ({
+    isProcessing: false,
+    error: {
+      isError: false,
+      message: '',
+    },
+    traineeList: {
+      headers: [
+        {
+          text: '#',
+          value: 'id',
+        },
+        {
+          text: '이름',
+          value: 'name',
+        },
+        {
+          text: '이메일',
+          value: 'email',
+        },
+        {
+          text: '연락처',
+          value: 'phone',
+        },
+        {
+          text: '',
+          value: 'action',
+          sortable: false,
+        },
+      ],
+      data: [],
+      searchKeyword: '',
+    },
+    schedule: {
+      program: {
+        title: '',
+      },
+      trainer: {
+        name: '',
+      },
+      maxTrainee: '',
+      startAt: '',
+      createdAt: '',
+      updatedAt: '',
+    },
+  }),
+
+  computed: {
+    id() {
+      if ('id' in this.$route.params) return this.$route.params.id;
+      return null;
+    },
+  },
+
+  methods: {
+    getSchedule(id) {
+      this.error.isError = false;
+      this.error.message = '';
+      this.isProcessing = true;
+
+      fetch(APISetting.endpoints.schedule.detail(id), APISetting.settings.get)
+        .then((res) => {
+          if (res.status === 404) return Promise.all([null, res]);
+          if ([200, 400, 500].includes(res.status)) return Promise.all([res.json(), res]);
+          throw new Error('알 수 없는 응답입니다.');
+        })
+        .then((values) => {
+          const [json, res] = values;
+          if (res.status === 404) throw new Error('존재하지 않는 데이터입니다.');
+          if (res.status !== 200) throw new Error(json.message);
+          this.schedule = json.schedule;
+        })
+        .catch((e) => {
+          this.error.message = e.message;
+          this.error.isError = true;
+        })
+        .finally(() => {
+          this.isProcessing = false;
+        });
+    },
+    updateSchedule(id) {
+      this.error.isError = false;
+      this.error.message = '';
+      this.isProcessing = true;
+
+      fetch(APISetting.endpoints.schedule.detail(id), APISetting.settings.put(this.schedule))
+        .then((res) => {
+          if (res.status === 404) return Promise.all([null, res]);
+          if ([200, 400, 404, 500].includes(res.status)) return Promise.all([res.json(), res]);
+          throw new Error('알 수 없는 응답입니다.');
+        })
+        .then((values) => {
+          const [json, res] = values;
+          if (res.status === 404) throw new Error('존재하지 않는 데이터입니다.');
+          if (res.status !== 200) throw new Error(json.message);
+          this.schedule = json.schedule;
+        })
+        .catch((e) => {
+          this.error.message = e.message;
+          this.error.isError = true;
+        })
+        .finally(() => {
+          this.isProcessing = false;
+        });
+    },
+    deleteSchedule(id) {
+      this.error.isError = false;
+      this.error.message = '';
+      this.isProcessing = true;
+
+      fetch(APISetting.endpoints.schedule.detail(id), APISetting.settings.delete)
+        .then((res) => {
+          if ([204, 404].includes(res.status)) return Promise.all([null, res]);
+          if ([204, 400, 404, 500].includes(res.status)) return Promise.all([res.json(), res]);
+          // If response status is not equal to 204, 400, 404, or 500, go to catch.
+          throw new Error('알 수 없는 응답입니다.');
+        })
+        .then((values) => {
+          const [json, res] = values;
+          if (res.status === 404) throw new Error('존재하지 않는 데이터입니다.');
+          if (res.status !== 204) throw new Error(json.message);
+          return this.$router.push('/schedule');
+        })
+        .catch((e) => {
+          this.error.message = e.message;
+          this.error.isError = true;
+        })
+        .finally(() => {
+          this.isProcessing = false;
+        });
+    },
+    editTrainee(id) {
+      this.$router.push(`/trainee/${id}`);
+    },
+  },
+
+  created() {
+    this.getSchedule(this.id);
+  },
+};
+</script>
-- 
GitLab