225 lines
8.9 KiB
TypeScript
225 lines
8.9 KiB
TypeScript
import { mdiCalendarCheck } from '@mdi/js'
|
|
import Head from 'next/head'
|
|
import React, { ReactElement } from 'react'
|
|
import { Field, Form, Formik } from 'formik'
|
|
import { useRouter } from 'next/router'
|
|
|
|
import BaseButton from '../../components/BaseButton'
|
|
import BaseButtons from '../../components/BaseButtons'
|
|
import BaseDivider from '../../components/BaseDivider'
|
|
import CardBox from '../../components/CardBox'
|
|
import FormField from '../../components/FormField'
|
|
import SectionMain from '../../components/SectionMain'
|
|
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
|
|
import { SelectField } from '../../components/SelectField'
|
|
import { SwitchField } from '../../components/SwitchField'
|
|
import { getPageTitle } from '../../config'
|
|
import { getRoleLaneFromUser } from '../../helpers/roleLanes'
|
|
import LayoutAuthenticated from '../../layouts/Authenticated'
|
|
import { create } from '../../stores/reservations/reservationsSlice'
|
|
import { useAppDispatch, useAppSelector } from '../../stores/hooks'
|
|
|
|
const initialValues = {
|
|
tenant: null,
|
|
organization: null,
|
|
booking_request: null,
|
|
reservation_code: '',
|
|
status: 'quoted',
|
|
property: null,
|
|
unit: null,
|
|
unit_type: null,
|
|
check_in_at: '',
|
|
check_out_at: '',
|
|
actual_check_in_at: '',
|
|
actual_check_out_at: '',
|
|
early_check_in: false,
|
|
late_check_out: false,
|
|
guest_count: '',
|
|
nightly_rate: '',
|
|
monthly_rate: '',
|
|
currency: 'USD',
|
|
internal_notes: '',
|
|
external_notes: '',
|
|
}
|
|
|
|
const ReservationsNew = () => {
|
|
const router = useRouter()
|
|
const dispatch = useAppDispatch()
|
|
const { currentUser } = useAppSelector((state) => state.auth)
|
|
|
|
const roleLane = getRoleLaneFromUser(currentUser)
|
|
const canManagePlatformFields = roleLane === 'super_admin'
|
|
const canManageFinancialFields = canManagePlatformFields || roleLane === 'admin'
|
|
const canManageOperations = roleLane !== 'customer'
|
|
const pageTitle = roleLane === 'customer' ? 'My Stay' : 'Create Stay'
|
|
const introCopy = canManagePlatformFields
|
|
? 'Create a stay record with full routing control, internal reference fields, and operational details.'
|
|
: canManageFinancialFields
|
|
? 'Create the stay record that will execute an approved request, assign housing, and prepare arrival details.'
|
|
: 'Create the stay record for operations. Platform routing and internal references stay managed in the background.'
|
|
|
|
const handleSubmit = async (values) => {
|
|
const payload = { ...values }
|
|
|
|
if (!canManagePlatformFields) {
|
|
delete payload.tenant
|
|
delete payload.organization
|
|
delete payload.reservation_code
|
|
}
|
|
|
|
if (!canManageFinancialFields) {
|
|
delete payload.nightly_rate
|
|
delete payload.monthly_rate
|
|
delete payload.internal_notes
|
|
}
|
|
|
|
await dispatch(create(payload))
|
|
await router.push('/reservations/reservations-list')
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>{getPageTitle(pageTitle)}</title>
|
|
</Head>
|
|
|
|
<SectionMain>
|
|
<SectionTitleLineWithButton icon={mdiCalendarCheck} title={pageTitle} main>
|
|
{''}
|
|
</SectionTitleLineWithButton>
|
|
|
|
<CardBox className='mx-auto max-w-6xl'>
|
|
<div className='mb-6 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-4 text-sm text-slate-600 dark:border-white/10 dark:bg-slate-900/40 dark:text-slate-300'>
|
|
{introCopy}
|
|
</div>
|
|
|
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
|
<Form>
|
|
<div className='grid gap-5 md:grid-cols-2'>
|
|
{canManagePlatformFields && (
|
|
<>
|
|
<FormField label='Tenant' labelFor='tenant'>
|
|
<Field name='tenant' id='tenant' component={SelectField} options={[]} itemRef={'tenants'} />
|
|
</FormField>
|
|
|
|
<FormField label='Organization' labelFor='organization'>
|
|
<Field name='organization' id='organization' component={SelectField} options={[]} itemRef={'organizations'} />
|
|
</FormField>
|
|
|
|
<FormField label='Reservation code'>
|
|
<Field name='reservation_code' placeholder='Reservation code' />
|
|
</FormField>
|
|
</>
|
|
)}
|
|
|
|
<FormField label='Source request' labelFor='booking_request'>
|
|
<Field name='booking_request' id='booking_request' component={SelectField} options={[]} itemRef={'booking_requests'} />
|
|
</FormField>
|
|
|
|
<FormField label='Status' labelFor='status'>
|
|
<Field name='status' id='status' component='select'>
|
|
<option value='quoted'>quoted</option>
|
|
<option value='confirmed'>confirmed</option>
|
|
<option value='checked_in'>checked_in</option>
|
|
<option value='checked_out'>checked_out</option>
|
|
<option value='canceled'>canceled</option>
|
|
<option value='no_show'>no_show</option>
|
|
</Field>
|
|
</FormField>
|
|
|
|
<FormField label='Property' labelFor='property'>
|
|
<Field name='property' id='property' component={SelectField} options={[]} itemRef={'properties'} />
|
|
</FormField>
|
|
|
|
<FormField label='Unit' labelFor='unit'>
|
|
<Field name='unit' id='unit' component={SelectField} options={[]} itemRef={'units'} />
|
|
</FormField>
|
|
|
|
<FormField label='Unit type' labelFor='unit_type'>
|
|
<Field name='unit_type' id='unit_type' component={SelectField} options={[]} itemRef={'unit_types'} />
|
|
</FormField>
|
|
|
|
<FormField label='Check-in'>
|
|
<Field type='datetime-local' name='check_in_at' />
|
|
</FormField>
|
|
|
|
<FormField label='Check-out'>
|
|
<Field type='datetime-local' name='check_out_at' />
|
|
</FormField>
|
|
|
|
{canManageOperations && (
|
|
<>
|
|
<FormField label='Actual check-in'>
|
|
<Field type='datetime-local' name='actual_check_in_at' />
|
|
</FormField>
|
|
|
|
<FormField label='Actual check-out'>
|
|
<Field type='datetime-local' name='actual_check_out_at' />
|
|
</FormField>
|
|
</>
|
|
)}
|
|
|
|
<FormField label='Guest count'>
|
|
<Field type='number' min='1' name='guest_count' placeholder='Guest count' />
|
|
</FormField>
|
|
|
|
<FormField label='Currency'>
|
|
<Field name='currency' placeholder='Currency' />
|
|
</FormField>
|
|
|
|
{canManageFinancialFields && (
|
|
<>
|
|
<FormField label='Nightly rate'>
|
|
<Field type='number' min='0' step='0.01' name='nightly_rate' placeholder='Nightly rate' />
|
|
</FormField>
|
|
|
|
<FormField label='Monthly rate'>
|
|
<Field type='number' min='0' step='0.01' name='monthly_rate' placeholder='Monthly rate' />
|
|
</FormField>
|
|
</>
|
|
)}
|
|
|
|
<FormField label='Early check-in' labelFor='early_check_in'>
|
|
<Field name='early_check_in' id='early_check_in' component={SwitchField} />
|
|
</FormField>
|
|
|
|
<FormField label='Late check-out' labelFor='late_check_out'>
|
|
<Field name='late_check_out' id='late_check_out' component={SwitchField} />
|
|
</FormField>
|
|
|
|
<div className='md:col-span-2'>
|
|
<FormField label='Guest-facing notes' hasTextareaHeight>
|
|
<Field name='external_notes' as='textarea' placeholder='Arrival details, move-in notes, or traveler-facing instructions' />
|
|
</FormField>
|
|
</div>
|
|
|
|
{canManageFinancialFields && (
|
|
<div className='md:col-span-2'>
|
|
<FormField label='Internal notes' hasTextareaHeight>
|
|
<Field name='internal_notes' as='textarea' placeholder='Internal coordination, pricing context, or escalation notes' />
|
|
</FormField>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<BaseDivider />
|
|
|
|
<BaseButtons>
|
|
<BaseButton type='submit' color='info' label='Save stay' />
|
|
<BaseButton type='reset' color='info' outline label='Reset' />
|
|
<BaseButton type='button' color='danger' outline label='Cancel' onClick={() => router.push('/reservations/reservations-list')} />
|
|
</BaseButtons>
|
|
</Form>
|
|
</Formik>
|
|
</CardBox>
|
|
</SectionMain>
|
|
</>
|
|
)
|
|
}
|
|
|
|
ReservationsNew.getLayout = function getLayout(page: ReactElement) {
|
|
return <LayoutAuthenticated permission={'CREATE_RESERVATIONS'}>{page}</LayoutAuthenticated>
|
|
}
|
|
|
|
export default ReservationsNew
|