diff --git a/src/components/NavigationDrawer.vue b/src/components/NavigationDrawer.vue index 3d4202e4a4749b7d5490b7c2eb48587965ae64c0..c247b9728334a28b0fc29cb34b310d69d183113f 100644 --- a/src/components/NavigationDrawer.vue +++ b/src/components/NavigationDrawer.vue @@ -48,6 +48,22 @@ </v-list-item-content> </v-list-item> </v-list-item-group> + <v-subheader>일정 관리</v-subheader> + <v-list-item-group + color="primary" + > + <v-list-item + link + :to="{ name: 'ScheduleList' }" + > + <v-list-item-icon> + <v-icon>mdi-shape</v-icon> + </v-list-item-icon> + <v-list-item-content> + <v-list-item-title>일정 목록</v-list-item-title> + </v-list-item-content> + </v-list-item> + </v-list-item-group> <v-subheader>매장 관리</v-subheader> <v-list-item-group color="primary" diff --git a/src/router/index.js b/src/router/index.js index 5829299ef8df2b987ba2e634334520b7e90f226a..21b2f13764fc21f7eaa54e93dd7520d5e2946c23 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -33,6 +33,11 @@ const routes = [ name: 'ProgramDetail', component: () => import('@/views/ProgramDetail.vue'), }, + { + path: '/schedule', + name: 'ScheduleList', + component: () => import('@/views/ScheduleList.vue'), + }, { path: '/trainee', name: 'TraineeList', diff --git a/src/settings/api.js b/src/settings/api.js index f1779dd8ce3b0c2943a587ca19db920f24f01ef9..df8a2ad7109002b5acfb10feea4e2130f5a0c2e3 100644 --- a/src/settings/api.js +++ b/src/settings/api.js @@ -14,6 +14,10 @@ export default { list: `${host}/program`, detail: id => (`${host}/program/${id}`), }, + schedule: { + list: `${host}/schedule`, + detail: id => (`${host}/schedule/${id}`), + }, trainee: { list: `${host}/trainee`, detail: id => (`${host}/trainee/${id}`), diff --git a/src/views/ScheduleList.vue b/src/views/ScheduleList.vue new file mode 100644 index 0000000000000000000000000000000000000000..eaee0d0a570e3936528b69212aa7b28876c8065b --- /dev/null +++ b/src/views/ScheduleList.vue @@ -0,0 +1,199 @@ +<template> + <div class="schedule-list"> + <v-card> + <v-card-title>일정 목록</v-card-title> + <v-card-text> + <v-row + justify="end" + > + <v-col + align-self="center" + cols="12" + sm="8" + md="9" + lg="9" + xl="10" + > + <v-btn + color="success" + outlined + @click="createSchedule" + > + <v-icon + left + > + mdi-plus + </v-icon> + 새 일정 등록 + </v-btn> + </v-col> + <v-col + cols="12" + sm="4" + md="3" + lg="3" + xl="2" + > + <v-text-field + append-icon="mdi-magnify" + clearable + label="일정 검색" + v-model="scheduleList.searchKeyword" + ></v-text-field> + </v-col> + </v-row> + <v-data-table + :headers="scheduleList.headers" + item-key="id" + :items="scheduleList.data" + :loading="isProcessing" + loading-text="데이터를 불러오는 중입니다." + :search="scheduleList.searchKeyword" + no-results-text="일치하는 일정을 찾지 못했습니다." + > + <template v-slot:item.action="{ item }"> + <v-icon + small + class="mr-2" + @click="editSchedule(item.id)" + > + mdi-pencil + </v-icon> + <v-icon + small + @click="deleteSchedule(item.id)" + > + mdi-delete + </v-icon> + </template> + </v-data-table> + </v-card-text> + </v-card> + </div> +</template> + +<script> +import moment from 'moment'; + +import APISetting from '@/settings/api'; + +export default { + name: 'ScheduleList', + + data: () => ({ + isProcessing: false, + error: { + isError: false, + message: '', + }, + scheduleList: { + headers: [ + { + text: '#', + value: 'id', + }, + { + text: '프로그램', + value: 'program.title', + }, + { + text: '트레이너', + value: 'trainer.name', + }, + { + text: '시작 시각', + value: 'startAt', + }, + { + text: '소요시간(분)', + value: 'program.durationTime', + }, + { + text: '최대 예약 가능 인원', + value: 'maxTrainee', + }, + { + text: '현재 예약 인원', + value: 'trainees.length', + }, + { + text: '현재 예약 가능 인원', + value: 'remainTrainee', + }, + { + text: '', + value: 'action', + sortable: false, + }, + ], + data: [], + searchKeyword: '', + }, + }), + + methods: { + getScheduleList() { + this.error.isError = false; + this.error.message = ''; + this.isProcessing = true; + + fetch(APISetting.endpoints.schedule.list, APISetting.settings.get) + .then((res) => { + if (res.status === 200) return res.json(); + throw new Error('알 수 없는 응답입니다.'); + }) + .then((json) => { + const { schedules } = json; + schedules.forEach((schedule) => { + // eslint-disable-next-line no-param-reassign + schedule.startAt = moment(schedule.startAt).format('YYYY-MM-DD HH:mm'); + // eslint-disable-next-line no-param-reassign + schedule.remainTrainee = schedule.maxTrainee - schedule.trainees.length; + }); + this.scheduleList.data = schedules; + }) + .catch((e) => { + this.error.message = e.message; + this.error.isError = true; + }) + .finally(() => { + this.isProcessing = false; + }); + }, + createSchedule() { + this.$router.push('/schedule/create'); + }, + editSchedule(id) { + this.$router.push(`/schedule/${id}`); + }, + deleteSchedule(id) { + this.error.isError = false; + this.error.message = ''; + this.isProcessing = true; + + fetch(APISetting.endpoints.schedule.detail(id), APISetting.settings.delete) + .then((res) => { + if (res.status === 204) return Promise.all([null, res]); + if ([400, 404, 500].includes(res.status)) return Promise.all([res.json(), res]); + throw new Error('알 수 없는 응답입니다.'); + }) + .then((values) => { + const [json, res] = values; + if (res.status === 204) return this.getScheduleList(); + throw new Error(json.message); + }) + .catch((e) => { + this.error.message = e.message; + this.error.isError = true; + }) + .finally(() => { + this.isProcessing = false; + }); + }, + }, + + created() { + this.getScheduleList(); + }, +}; +</script>