Loading...

Animal Villa

animalvilla.org

{{ authError }}

Animal Villa

animalvilla.org

A new version is available.

No animals currently boarding

Check in a pet to get started

🐶

{{ dog.dogName }}

Boarding {{ visitCountMap[getDogKey(dog.dogName, dog.ownerName)] || 1 }} visit{{ (visitCountMap[getDogKey(dog.dogName, dog.ownerName)] || 1) !== 1 ? 's' : '' }}

Owner: {{ dog.ownerName }}

Breed

{{ dog.breed }}

Age

{{ dog.age }}

Checked In

{{ formatDate(dog.checkInDate) }}

Expected Out

{{ formatDate(dog.expectedCheckOut) }}

${{ dog.ratePerNight }}/night

${{ getVisitRevenue(dog).toFixed(2) }}

{{ getEstimatedTotal(dog.checkInDate, dog.expectedCheckOut, dog.ratePerNight) }} nights

Tip: ${{ dog.tip }} Fee: ${{ dog.dropPickFee }}
Tip: Fee:

No daily rate set

Notes

{{ dog.notes }}

📱 {{ dog.ownerPhone }} 🕐 {{ formatRelative(dog.checkInDate) }}

Check In a New Pet

🐶

{{ dog.dogName }}

{{ dog.ownerName }} · {{ dog.breed }}

{{ dog.visitCount }} visit{{ dog.visitCount !== 1 ? 's' : '' }}
Dog photo

Add photo

×
Estimated Total ${{ calculatedTotal.toFixed(2) }}

${{ form.ratePerNight || 0 }}/night × {{ calculatedNights }} night{{ calculatedNights !== 1 ? 's' : '' }}

Tip${{ form.tip }}
Drop/Pick Fee${{ form.dropPickFee }}
{{ uploadedDocName }}

No conversations yet

Chat threads appear when you check in a dog

{{ chatVisits.length }} conversation{{ chatVisits.length !== 1 ? 's' : '' }}

🐶

{{ cv.visit.dogName }}

{{ cv.visit.ownerName }}

{{ formatMessageTime(cv.lastMessage.createdAt) }}

{{ cv.unreadCount }}

{{ cv.lastMessage.senderId === user.uid ? 'You' : cv.visit.ownerName }}: {{ cv.lastMessage.text }}

No messages yet — start the conversation

🐶

{{ selectedChatVisit.dogName }}

{{ selectedChatVisit.ownerName }}

No messages yet. Say hello!

{{ msg.text }}

{{ formatMessageTime(msg.createdAt) }}

No history yet

Completed visits will appear here

{{ history.length }} completed visit{{ history.length !== 1 ? 's' : '' }}

🐶

{{ visit.dogName }}

Completed {{ visitCountMap[getDogKey(visit.dogName, visit.ownerName)] || 1 }} visit{{ (visitCountMap[getDogKey(visit.dogName, visit.ownerName)] || 1) !== 1 ? 's' : '' }}

{{ visit.ownerName }}

{{ formatDate(visit.checkInDate) }} → {{ formatDate(visit.checkOutDate) }}

{{ getStayDuration(visit.checkInDate, visit.checkOutDate) }}

${{ getVisitRevenue(visit).toFixed(2) }}

+${{ visit.tip }} tip +${{ visit.dropPickFee }} fee
{{ visit.notes }}
No rate
Tip: Fee:
{{ calendarImportResult }}

Calendar Sync Error

{{ calendarError }}

{{ calendarMonthName }} {{ calendarYear }}

{{ day }}
{{ cell.day }}
{{ visit.dogName }}

Google Calendar Sync

Last sync: {{ formatMessageTime(lastSyncTime) }}
Connect Google Calendar to sync boarding visits.
Google Calendar connected · {{ googleCalendars.length }} calendar{{ googleCalendars.length !== 1 ? 's' : '' }} available
{{ syncConfig.autoSync ? 'On' : 'Off' }}
Export mode: When you check in a pet, a calendar event will be created automatically. Check-outs will update the event.
Auto-sync is active — syncing every {{ syncConfig.intervalMinutes }} minute{{ syncConfig.intervalMinutes !== 1 ? 's' : '' }}
ICS File Import (alternative)

Export your calendar as an ICS file and upload it here. Events with title format PetName (OwnerName) will be detected.

{{ uploadedICSName }}
or paste ICS text

{{ filteredEvents.length }} of {{ calendarEvents.length }} Event{{ calendarEvents.length !== 1 ? 's' : '' }}

Filtered by "{{ calendarFilter }}"

{{ ev.petName }}

Owner: {{ ev.ownerName }}

📅 {{ ev.checkInDate }} {{ ev.checkInTime || '' }} 📆 {{ ev.checkOutDate }} {{ ev.checkOutTime || '' }} {{ ev.originalTitle }}

{{ yearToDateRevenue.year }} Revenue

Total Revenue

${{ yearToDateRevenue.total.toFixed(2) }}

{{ yearToDateRevenue.completedVisits }} completed

Currently Boarding

${{ yearToDateRevenue.pendingTotal.toFixed(2) }}

{{ yearToDateRevenue.activeVisits }} active

Avg per Visit

${{ yearToDateRevenue.avgPerVisit.toFixed(2) }}

{{ yearToDateRevenue.totalVisits }} total

Total Tips

${{ yearToDateRevenue.totalTips.toFixed(2) }}

{{ yearToDateRevenue.tippedVisits }} visit{{ yearToDateRevenue.tippedVisits !== 1 ? 's' : '' }} with tips

{{ lastYearRevenue.year }} Revenue

Total Revenue

${{ lastYearRevenue.total.toFixed(2) }}

{{ lastYearRevenue.completedVisits }} completed

Avg per Visit

${{ lastYearRevenue.avgPerVisit.toFixed(2) }}

{{ lastYearRevenue.totalVisits }} total

Total Tips

${{ lastYearRevenue.totalTips.toFixed(2) }}

{{ lastYearRevenue.tippedVisits }} visit{{ lastYearRevenue.tippedVisits !== 1 ? 's' : '' }} with tips

vs. {{ yearToDateRevenue.year }}

{{ yearToDateRevenue.total > 0 ? (((yearToDateRevenue.total - lastYearRevenue.total) / lastYearRevenue.total * 100) || 0).toFixed(1) + '%' : 'N/A' }}

change

Completed Visits ({{ yearToDateRevenue.year }})

Total: ${{ yearToDateRevenue.total.toFixed(2) }}

No completed visits this year
🐶

{{ v.dogName }}

{{ v.ownerName }} · {{ formatDate(v.checkInDate) }} → {{ formatDate(v.checkOutDate) }}

${{ getVisitRevenue(v).toFixed(2) }}

${{ v.ratePerNight }}/night × {{ getVisitNights(v) }}

Tip: ${{ v.tip }}

Fee: ${{ v.dropPickFee }}

No rate set

Check Out {{ selectedDog?.dogName }}

Confirm check-out details

Final Total ${{ calcCheckoutTotal().toFixed(2) }}

{{ calcCheckoutNights() }} night{{ calcCheckoutNights() !== 1 ? 's' : '' }} × ${{ selectedDog.ratePerNight }}/night

Tip${{ selectedDog.tip }}
Drop/Pick Fee${{ selectedDog.dropPickFee }}

Edit Visit

Update details for {{ editForm.dogName || 'this visit' }}

Dog photo

Add photo

×
{{ editFormDocName }}
🐶

{{ selectedDogProfile.dogName }}

Owner: {{ selectedDogProfile.ownerName }}

{{ dogProfileVisits.length }} visit{{ dogProfileVisits.length !== 1 ? 's' : '' }} total

${{ dogProfileTotal.toFixed(2) }}

total revenue

Breed{{ selectedDogProfile.breed }}
Age{{ selectedDogProfile.age }}
Phone{{ selectedDogProfile.ownerPhone }}
Rate${{ selectedDogProfile.ratePerNight }}/night

Notes

{{ selectedDogProfile.notes }}

Visit History

No visits found
{{ v.status === 'checked_in' ? 'Boarding' : 'Completed' }} ${{ v.ratePerNight }}/night

{{ formatDate(v.checkInDate) }} → {{ formatDate(v.checkOutDate) }}

${{ getVisitRevenue(v).toFixed(2) }}

+${{ v.tip }} tip +${{ v.dropPickFee }} fee

Total

${{ dogProfileTotal.toFixed(2) }}

Delete Visit

Are you sure you want to delete the visit for {{ visitToDelete?.dogName }} ({{ visitToDelete?.ownerName }})? This action cannot be undone.

{{ viewerData.title }}

No preview available

{{ viewerData.title }}

Download

{{ formatDate(selectedDayDateStr) }}

No visits on this day
🐶

{{ visit.dogName }}

{{ visit.status === 'checked_in' ? 'Boarding' : 'Completed' }}

Owner: {{ visit.ownerName }}

{{ formatDate(visit.checkInDate) }} → {{ formatDate(visit.expectedCheckOut) }}

{{ visit.notes }}
{{ toast.message }}