Files
bobu/bobu/app/pages/wadiz/[docId].vue
2025-07-15 11:23:20 +09:00

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>