448 lines
15 KiB
Vue
448 lines
15 KiB
Vue
<template>
|
|
<div class="mx-auto max-w-5xl w-full py-4 px-6 items-center">
|
|
<div class="border-b border-gray-200 dark:border-gray-700 pb-8">
|
|
<div class="mx-auto mt-10 max-w-lg space-y-8">
|
|
<!-- 1) 와디즈 결제 번호 -->
|
|
<div>
|
|
<label
|
|
for="paymentId"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
와디즈 결제 번호
|
|
</label>
|
|
<VeeField
|
|
name="paymentId"
|
|
id="paymentId"
|
|
type="text"
|
|
rules="required"
|
|
v-model="board.paymentId"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage name="paymentId" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 2) 성함 -->
|
|
<div>
|
|
<label
|
|
for="name"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
성함
|
|
</label>
|
|
<VeeField
|
|
name="name"
|
|
id="name"
|
|
type="text"
|
|
rules="required"
|
|
v-model="board.name"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage name="name" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 3) 주소 -->
|
|
<div>
|
|
<label
|
|
for="address"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
주소
|
|
</label>
|
|
<VeeField
|
|
name="address"
|
|
id="address"
|
|
type="text"
|
|
rules="required"
|
|
v-model="board.address"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600"
|
|
/>
|
|
<VeeErrorMessage name="address" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 4) 메일 -->
|
|
<div>
|
|
<label
|
|
for="email"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
메일
|
|
</label>
|
|
<VeeField
|
|
name="email"
|
|
id="email"
|
|
type="email"
|
|
rules="required|email"
|
|
v-model="board.email"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage name="email" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 5) 본인 연락처 -->
|
|
<div>
|
|
<label
|
|
for="phone"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
본인 연락처 (Your Phone)
|
|
</label>
|
|
<VeeField
|
|
name="phone"
|
|
id="phone"
|
|
type="tel"
|
|
rules="required"
|
|
v-model="board.phone"
|
|
placeholder="010-1234-5678"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage name="phone" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
<!-- 6) 긴급 연락처 -->
|
|
<div>
|
|
<label
|
|
for="emergencyPhone"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
긴급 연락처 | 본인 외 제3자
|
|
</label>
|
|
<VeeField
|
|
name="emergencyPhone"
|
|
id="emergencyPhone"
|
|
type="tel"
|
|
rules="required"
|
|
v-model="board.emergencyPhone"
|
|
placeholder="010-0000-0000"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600"
|
|
/>
|
|
<VeeErrorMessage
|
|
name="emergencyPhone"
|
|
class="text-red-500 text-sm mt-1"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 7) 예약 일정 선택 -->
|
|
<div>
|
|
<label
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
예약 일정 선택
|
|
</label>
|
|
<div class="mt-2 flex gap-4">
|
|
<!-- 시작일 -->
|
|
<div class="w-1/2">
|
|
<label
|
|
for="scheduleStart"
|
|
class="mb-1 block text-sm text-gray-700 dark:text-gray-300"
|
|
>
|
|
시작일
|
|
</label>
|
|
</div>
|
|
|
|
<!-- 종료일 -->
|
|
<div class="w-1/2">
|
|
<label
|
|
for="scheduleEnd"
|
|
class="mb-1 block text-sm text-gray-700 dark:text-gray-300"
|
|
>
|
|
종료일
|
|
</label>
|
|
<input
|
|
v-model="board.scheduleEnd"
|
|
id="scheduleEnd"
|
|
type="text"
|
|
disabled
|
|
placeholder="자동 계산 (1박 2일)"
|
|
class="w-full rounded-md bg-gray-100 dark:bg-gray-700 px-3 py-2 text-gray-400 dark:text-gray-400 border dark:border-gray-600"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
예약불가 날짜: 2025년 10월 3일(금)~5일(일), 연박 불가 / 체류가능일 :
|
|
금·토·일
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 8) 본인 참석 여부 -->
|
|
<div>
|
|
<span
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
본인 참석 여부 (Are you attending?)
|
|
</span>
|
|
<div class="mt-2 flex items-center gap-6">
|
|
<label class="inline-flex items-center">
|
|
<VeeField
|
|
name="attending"
|
|
type="radio"
|
|
:value="'yes'"
|
|
v-model="board.attending"
|
|
class="h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500"
|
|
/>
|
|
<span class="ml-2 text-gray-700 dark:text-gray-300"
|
|
>예 (Yes)</span
|
|
>
|
|
</label>
|
|
<label class="inline-flex items-center">
|
|
<VeeField
|
|
name="attending"
|
|
type="radio"
|
|
:value="'no'"
|
|
v-model="board.attending"
|
|
class="h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500"
|
|
/>
|
|
<span class="ml-2 text-gray-700 dark:text-gray-300"
|
|
>아니오 (No)</span
|
|
>
|
|
</label>
|
|
</div>
|
|
<VeeErrorMessage name="attending" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 8-2) If "아니오", show alternate attendee fields -->
|
|
<div v-if="board.attending === 'no'" class="space-y-4">
|
|
<!-- 대리 참석자 성함 -->
|
|
<div>
|
|
<label
|
|
for="altName"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
대리 참석자 성함 (Alternate Attendee Name)
|
|
</label>
|
|
<VeeField
|
|
name="altName"
|
|
id="altName"
|
|
type="text"
|
|
v-model="board.altName"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage name="altName" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- 대리 참석자 연락처 -->
|
|
<div>
|
|
<label
|
|
for="altPhone"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
대리 참석자 연락처 (Alternate Attendee Phone)
|
|
</label>
|
|
<VeeField
|
|
name="altPhone"
|
|
id="altPhone"
|
|
type="tel"
|
|
v-model="board.altPhone"
|
|
placeholder="010-1234-5678"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border outline-1 outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:outline-indigo-600"
|
|
/>
|
|
<VeeErrorMessage
|
|
name="altPhone"
|
|
class="text-red-500 text-sm mt-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 9) 참여 동반자 정보 -->
|
|
<div>
|
|
<label
|
|
for="companions"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
참여 동반자 정보 (선택)
|
|
</label>
|
|
<VeeField
|
|
name="companions"
|
|
id="companions"
|
|
type="text"
|
|
v-model="board.companions"
|
|
placeholder="동반자 성함 등"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border dark:border-gray-600"
|
|
/>
|
|
<VeeErrorMessage
|
|
name="companions"
|
|
class="text-red-500 text-sm mt-1"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 10) 알레르기나 건강 특이사항 (선택) -->
|
|
<div>
|
|
<label
|
|
for="healthNotes"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
알레르기나 건강 특이사항 (선택)
|
|
</label>
|
|
<VeeField
|
|
name="healthNotes"
|
|
id="healthNotes"
|
|
v-model="board.healthNotes"
|
|
as="textarea"
|
|
rows="2"
|
|
placeholder="예: 견과류 알레르기"
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border dark:border-gray-600"
|
|
/>
|
|
<VeeErrorMessage
|
|
name="healthNotes"
|
|
class="text-red-500 text-sm mt-1"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 11) 전달하고 싶은 말 (선택) -->
|
|
<div>
|
|
<label
|
|
for="remarks"
|
|
class="block text-sm font-medium text-gray-900 dark:text-gray-100"
|
|
>
|
|
전달하고 싶은 말 (선택)
|
|
</label>
|
|
<VeeField
|
|
name="remarks"
|
|
id="remarks"
|
|
v-model="board.remarks"
|
|
as="textarea"
|
|
rows="4"
|
|
placeholder="예: 채팅으로 연락해주세요."
|
|
class="mt-1 block w-full rounded-md bg-white dark:bg-gray-800 px-3.5 py-2 text-base text-gray-900 dark:text-gray-100 border dark:border-gray-600"
|
|
/>
|
|
<VeeErrorMessage name="remarks" class="text-red-500 text-sm mt-1" />
|
|
</div>
|
|
|
|
<!-- Submit -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, watch, onMounted } from 'vue';
|
|
import useUserStore from '@/stores/user';
|
|
import AppBoardHeader from '@/components/boards/BoardHeader.vue';
|
|
import AppBoardAction from '@/components/boards/BoardAction.vue';
|
|
import AppBoardBody from '@/components/boards/BoardBody.vue';
|
|
import AppLoadingOverlay from '@/components/LoadingOverlay.vue';
|
|
import { loadBoardDetails, deleteSingle } from '@/utils/boardUtils';
|
|
import type {
|
|
BoardItem,
|
|
OrderByDirection,
|
|
WadizBoard,
|
|
FileItem,
|
|
} from '@/types';
|
|
const { $firebase } = useNuxtApp();
|
|
const storage = $firebase.storage;
|
|
|
|
//***Variables, Things you need to change****)
|
|
const wadizesCollection = $firebase.wadizesCollection;
|
|
import AppUploadWadizForm from '@/components/boards/wadiz/UploadWadizForm.vue';
|
|
import AppUploadWadiz from '@/pages/wadiz/upload.vue';
|
|
const currentCollection = wadizesCollection;
|
|
const currentBoardRouteName = '/wadiz';
|
|
const compData = {
|
|
title: '와디즈 | WADIZ',
|
|
routeName: '/wadiz/[docId]', // path to single notice view
|
|
itemsPerPage: 20,
|
|
defaultSort: 'desc' as OrderByDirection,
|
|
listRouteName: '/wadiz',
|
|
uploadRouteName: '/wadiz/upload',
|
|
};
|
|
|
|
//
|
|
const in_submission = ref(false);
|
|
const deletingMessage = '삭제 중! 잠시만 기다려주세요...';
|
|
|
|
//Reactive variables
|
|
const router = useRouter();
|
|
const route = useRoute(); // Access the route object
|
|
const docId = computed(() => route.params.docId as string);
|
|
|
|
const board: Ref<WadizBoard> = ref({
|
|
docId: '',
|
|
userId: '',
|
|
title: '',
|
|
description: '',
|
|
boardState: { state: 'processing' },
|
|
announcement: false,
|
|
created: '',
|
|
files: [],
|
|
//depreciated
|
|
boards_number: 0,
|
|
thumbnail: { name: '', url: '' } as FileItem,
|
|
ishidden: false,
|
|
//wadizes
|
|
name: '',
|
|
paymentId: '',
|
|
email: '',
|
|
phone: '',
|
|
address: '',
|
|
emergencyPhone: '',
|
|
scheduleStart: '',
|
|
scheduleEnd: '',
|
|
attending: 'yes',
|
|
altName: '',
|
|
altPhone: '',
|
|
companions: '',
|
|
healthNotes: '',
|
|
remarks: '',
|
|
});
|
|
|
|
const userStore = useUserStore();
|
|
const userRole = computed(() => userStore.userRole);
|
|
|
|
const toggleEdit = ref(false);
|
|
|
|
const handleDelete = async () => {
|
|
const result = await deleteSingle(board.value, currentCollection, storage);
|
|
if (result) {
|
|
router.push(currentBoardRouteName); // path string works directly in Nuxt3
|
|
}
|
|
};
|
|
|
|
const handleUpdateSuccess = async () => {
|
|
await loadBoardDetails(
|
|
board.value.docId,
|
|
board, // ref<BoardItem>
|
|
currentCollection,
|
|
router,
|
|
currentBoardRouteName,
|
|
userRole // Ref<number>
|
|
);
|
|
|
|
toggleEdit.value = false;
|
|
};
|
|
|
|
const toggleEditBoard = () => {
|
|
toggleEdit.value = !toggleEdit.value;
|
|
};
|
|
|
|
watch(docId, (newId) => {
|
|
if (!newId) return;
|
|
console.log('newId', newId);
|
|
loadBoardDetails(
|
|
newId,
|
|
board,
|
|
currentCollection,
|
|
router,
|
|
currentBoardRouteName,
|
|
userRole
|
|
);
|
|
});
|
|
onMounted(async () => {
|
|
if (docId.value) {
|
|
await loadBoardDetails(
|
|
docId.value,
|
|
board,
|
|
currentCollection,
|
|
router,
|
|
currentBoardRouteName,
|
|
userRole
|
|
);
|
|
// Anything here will run AFTER board.value has real data
|
|
console.log('after await:', board.value);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.board {
|
|
word-wrap: break-word;
|
|
}
|
|
</style>
|