Skip to content

일정 CRUD 모달

ScheduleFormModal은 일정 생성·수정을 위한 Dialog 컴포넌트입니다. draft → Schedule 변환(buildScheduleFromDraft)을 내부에서 처리하고, 완성된 Schedulesubmit emit으로 전달합니다 — 소비자는 upsertSchedule/removeSchedule로 목록만 갱신하면 됩니다.

기본 사용

vue
<script setup lang="ts">
import { ref } from 'vue'
import {
  ScheduleCalendar,
  ScheduleFormModal,
  useScheduleCalendarHost,
  upsertSchedule,
  removeSchedule,
} from '@vuepkg/calendar'
import type { Schedule, Participant } from '@vuepkg/calendar'

const schedules = ref<Schedule[]>([])
const participants: Participant[] = [
  { id: 'user-hong', name: '홍길동' },
  { id: 'user-kim', name: '김민수' },
]

const modalOpen = ref(false)
const modalMode = ref<'create' | 'edit'>('create')
const editingSchedule = ref<Schedule | null>(null)
const initialStart = ref<Date | undefined>()
const initialEnd = ref<Date | undefined>()

const { view, date, calendarListeners } = useScheduleCalendarHost({
  onQueryChange(payload) {
    // API 호출
  },
  onScheduleClick({ schedule }) {
    // 클릭 → 수정 모달
    modalMode.value = 'edit'
    editingSchedule.value = schedule
    modalOpen.value = true
  },
  onTimeSlotSelect({ start, end }) {
    // 빈 셀 클릭 → 생성 모달
    modalMode.value = 'create'
    editingSchedule.value = null
    initialStart.value = start
    initialEnd.value = end
    modalOpen.value = true
  },
})

function handleSubmit(schedule: Schedule) {
  schedules.value = upsertSchedule(schedules.value, schedule)
  modalOpen.value = false
}

function handleDelete(scheduleId: string) {
  schedules.value = removeSchedule(schedules.value, scheduleId)
  modalOpen.value = false
}
</script>

<template>
  <div style="height: 600px;">
    <ScheduleCalendar
      v-model:view="view"
      v-model:date="date"
      :schedules="schedules"
      v-bind="calendarListeners"
    />
  </div>

  <ScheduleFormModal
    :open="modalOpen"
    :mode="modalMode"
    :schedule="editingSchedule"
    :initial-start="initialStart"
    :initial-end="initialEnd"
    :participants="participants"
    :existing-schedules="schedules"
    @close="modalOpen = false"
    @submit="handleSubmit"
    @delete="handleDelete"
  />
</template>

ScheduleDraft 타입

ScheduleFormModal이 내부 폼 상태를 다룰 때 쓰는 타입입니다. Schedule과 달리 participantId가 필수입니다(내장 폼은 항상 참가자 선택을 요구).

ts
interface ScheduleDraft {
  id?: string
  title: string
  type: string
  participantId: string
  start: Date
  end: Date
  allDay: boolean
  recurrence?: RecurrenceRule
}

헬퍼 함수

ScheduleFormModal을 그대로 쓰면 buildScheduleFromDraft를 직접 호출할 필요가 없습니다(모달이 내부에서 처리). 헤드리스로 직접 폼을 구현할 때만 사용하세요.

ts
import {
  createScheduleId,       // 순차 id 생성 — 's-001', 's-002', ... (UUID 아님)
  buildScheduleFromDraft, // (draft, participants, existing) => Schedule — id 자동 할당
  upsertSchedule,         // (list, schedule) => Schedule[] — id 있으면 교체, 없으면 추가
  removeSchedule,         // (list, scheduleId) => Schedule[]
} from '@vuepkg/calendar'

// 헤드리스 생성 예 — 직접 만든 폼에서 draft를 받은 경우
const schedule = buildScheduleFromDraft(draft, participants, schedules.value)
schedules.value = upsertSchedule(schedules.value, schedule)

// 삭제
schedules.value = removeSchedule(schedules.value, id)

국제화 (i18n)

ScheduleFormModal의 기본 텍스트는 한국어입니다. 다른 언어가 필요하면 labels prop에 필요한 필드만 오버라이드하세요 — 미지정 필드는 한국어 기본값을 유지합니다.

vue
<ScheduleFormModal
  :labels="{ createTitle: 'Create Event', editTitle: 'Edit Event', titleField: 'Title' }"
  ...
/>

폼 레이아웃 자체(필드 재배치, 다른 위젯)를 바꿔야 한다면 라벨 오버라이드로는 부족합니다 — 위 "헬퍼 함수" 섹션처럼 buildScheduleFromDraft + 직접 구현한 폼을 사용하세요. 전체 필드 목록은 ScheduleFormModal API 레퍼런스를 참고하세요.

ScheduleFormModal API

전체 Props/Emits 표는 ScheduleFormModal API 레퍼런스를 참고하세요. 핵심만 요약하면:

  • open은 v-model이 아닙니다open prop을 직접 갱신하고 @close로 닫기 요청을 받습니다.
  • participants는 필수 prop입니다.
  • 저장 결과는 @submit으로 완성된 Schedule을 받습니다(ScheduleDraft가 아님).

Released under the MIT License.