feat(lifecycle): parent view with TabBar + 3 stub children routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rodribm10 2026-04-15 10:51:09 -03:00
parent bc85ec0a67
commit 65a76ed59d
6 changed files with 216 additions and 0 deletions

View File

@ -17,6 +17,10 @@ import ResponsesIndex from './responses/Index.vue';
import ResponsesPendingIndex from './responses/Pending.vue';
import CustomToolsIndex from './tools/Index.vue';
import ReservationsIndex from './reservations/Index.vue';
import LifecycleIndex from './lifecycle/Index.vue';
import LifecycleRules from './lifecycle/Rules.vue';
import LifecycleSettings from './lifecycle/Settings.vue';
import LifecycleHistory from './lifecycle/History.vue';
const meta = {
permissions: ['administrator', 'agent'],
@ -137,4 +141,30 @@ export const routes = [
name: 'captain_reservations_index',
meta,
},
{
path: frontendURL('accounts/:accountId/captain/lifecycle'),
component: LifecycleIndex,
meta,
redirect: { name: 'captain_lifecycle_rules' },
children: [
{
path: 'rules',
component: LifecycleRules,
name: 'captain_lifecycle_rules',
meta,
},
{
path: 'settings',
component: LifecycleSettings,
name: 'captain_lifecycle_settings',
meta,
},
{
path: 'history',
component: LifecycleHistory,
name: 'captain_lifecycle_history',
meta,
},
],
},
];

View File

@ -0,0 +1,14 @@
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="p-6">
<h2 class="text-lg font-semibold">
{{ t('CAPTAIN_LIFECYCLE.TABS.HISTORY') }}
</h2>
<!-- Implementation in Task 13 -->
</div>
</template>

View File

@ -0,0 +1,52 @@
<script setup>
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import PageLayout from 'dashboard/components-next/captain/PageLayout.vue';
import TabBar from 'dashboard/components-next/tabbar/TabBar.vue';
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const tabs = computed(() => [
{ name: 'captain_lifecycle_rules', label: t('CAPTAIN_LIFECYCLE.TABS.RULES') },
{
name: 'captain_lifecycle_settings',
label: t('CAPTAIN_LIFECYCLE.TABS.SETTINGS'),
},
{
name: 'captain_lifecycle_history',
label: t('CAPTAIN_LIFECYCLE.TABS.HISTORY'),
},
]);
const activeIndex = computed(() =>
Math.max(
0,
tabs.value.findIndex(tab => tab.name === route.name)
)
);
const handleTabChanged = tab => {
router.push({ name: tab.name, params: route.params });
};
</script>
<template>
<PageLayout
:header-title="t('CAPTAIN_LIFECYCLE.HEADER')"
:show-assistant-switcher="false"
:show-pagination-footer="false"
:show-know-more="false"
>
<div class="flex flex-col gap-4">
<TabBar
:tabs="tabs"
:initial-active-tab="activeIndex"
@tab-changed="handleTabChanged"
/>
<router-view />
</div>
</PageLayout>
</template>

View File

@ -0,0 +1,14 @@
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="p-6">
<h2 class="text-lg font-semibold">
{{ t('CAPTAIN_LIFECYCLE.TABS.RULES') }}
</h2>
<!-- Implementation in Task 15 -->
</div>
</template>

View File

@ -0,0 +1,14 @@
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<div class="p-6">
<h2 class="text-lg font-semibold">
{{ t('CAPTAIN_LIFECYCLE.TABS.SETTINGS') }}
</h2>
<!-- Implementation in Task 14 -->
</div>
</template>

View File

@ -0,0 +1,92 @@
export const EVENTS = [
{
value: 'reservation.confirmed',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.EVENTS.RESERVATION_CONFIRMED',
},
{
value: 'checkin.scheduled_at',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.EVENTS.CHECKIN_SCHEDULED_AT',
},
{
value: 'checkout.scheduled_at',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.EVENTS.CHECKOUT_SCHEDULED_AT',
},
{
value: 'reservation.cancelled',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.EVENTS.RESERVATION_CANCELLED',
},
{
value: 'reservation.no_show',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.EVENTS.RESERVATION_NO_SHOW',
},
];
export const MESSAGE_TYPES = [
{
value: 'text',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.MESSAGE_TYPES.TEXT',
},
{
value: 'buttons',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.MESSAGE_TYPES.BUTTONS',
},
{
value: 'list',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.MESSAGE_TYPES.LIST',
},
{
value: 'url_button',
labelKey: 'CAPTAIN_LIFECYCLE.RULES.WIZARD.MESSAGE_TYPES.URL_BUTTON',
},
];
export const OFFSET_UNITS = [
{ value: 'minutes', factor: 1 },
{ value: 'hours', factor: 60 },
{ value: 'days', factor: 1440 },
];
export const AVAILABLE_VARIABLES = [
{ key: 'customer.first_name', descKey: 'Primeiro nome do cliente' },
{ key: 'customer.name', descKey: 'Nome completo' },
{ key: 'customer.phone', descKey: 'Telefone' },
{ key: 'reservation.suite', descKey: 'Suíte' },
{ key: 'reservation.unit_name', descKey: 'Nome da unidade' },
{ key: 'reservation.check_in_at', descKey: 'Check-in' },
{ key: 'reservation.check_out_at', descKey: 'Check-out' },
{ key: 'reservation.amount', descKey: 'Valor' },
{ key: 'hotel.wifi_password', descKey: 'Senha do WiFi' },
{ key: 'hotel.menu_link', descKey: 'Link do cardápio' },
{ key: 'hotel.google_review_link', descKey: 'Link de review' },
{ key: 'hotel.address', descKey: 'Endereço' },
];
export const RULE_TEMPLATES = [
{
id: 'precheckin_reminder',
name: 'Lembrete 10min antes do check-in',
event: 'checkin.scheduled_at',
offset_minutes: -10,
message_type: 'text',
message_body:
'Oi {{ customer.first_name }}! Seu check-in é em 10 minutos na suíte {{ reservation.suite }}. Wifi: {{ hotel.wifi_password }}',
},
{
id: 'welcome_instay',
name: 'Boas-vindas após check-in',
event: 'checkin.scheduled_at',
offset_minutes: 15,
message_type: 'text',
message_body:
'Seja bem-vindo(a), {{ customer.first_name }}! Qualquer coisa, só chamar. Cardápio: {{ hotel.menu_link }}',
},
{
id: 'review_request',
name: 'Pedido de review no Google',
event: 'checkout.scheduled_at',
offset_minutes: 120,
message_type: 'url_button',
message_body:
'{{ customer.first_name }}, adoraríamos saber como foi sua estadia. Se puder, deixa um review pra gente: {{ hotel.google_review_link }}',
},
];