|
|
@@ -3,8 +3,9 @@
|
|
|
<DefaultHeaderPage />
|
|
|
|
|
|
<div v-if="!isLoading" class="q-pa-md">
|
|
|
+ <!-- Tabs Principais: Agendamentos / Oportunidades -->
|
|
|
<q-tabs
|
|
|
- v-model="viewMode"
|
|
|
+ v-model="scheduleType"
|
|
|
dense
|
|
|
class="text-grey"
|
|
|
active-color="primary"
|
|
|
@@ -12,189 +13,443 @@
|
|
|
align="justify"
|
|
|
narrow-indicator
|
|
|
>
|
|
|
- <q-tab name="client" :label="$t('schedules.view_as_client')" icon="person" />
|
|
|
- <q-tab name="provider" :label="$t('schedules.view_as_provider')" icon="work" />
|
|
|
+ <q-tab name="default" :label="$t('ui.navigation.schedules')" icon="event" />
|
|
|
+ <q-tab name="custom" :label="$t('ui.navigation.opportunities')" icon="mdi-bullseye-arrow" />
|
|
|
</q-tabs>
|
|
|
|
|
|
<q-separator />
|
|
|
|
|
|
- <q-tab-panels v-model="viewMode" animated>
|
|
|
- <q-tab-panel name="client">
|
|
|
- <div class="row q-col-gutter-md">
|
|
|
- <div class="col-12">
|
|
|
- <q-select
|
|
|
- v-model="statusFilter"
|
|
|
- :options="statusFilterOptions"
|
|
|
- :label="$t('schedules.filter_by_status')"
|
|
|
- outlined
|
|
|
- dense
|
|
|
- emit-value
|
|
|
- map-options
|
|
|
- clearable
|
|
|
- class="q-mb-md"
|
|
|
- style="max-width: 300px"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="col-12">
|
|
|
- <q-expansion-item
|
|
|
- v-for="clientGroup in filteredGroupedSchedules"
|
|
|
- :key="clientGroup.client_id"
|
|
|
- :label="clientGroup.client_name"
|
|
|
- icon="person"
|
|
|
- header-class="bg-primary text-white"
|
|
|
- expand-icon-class="text-white"
|
|
|
- class="q-mb-md shadow-2 rounded-borders"
|
|
|
- default-opened
|
|
|
- >
|
|
|
- <q-card>
|
|
|
- <q-card-section>
|
|
|
- <q-list bordered separator>
|
|
|
- <q-item
|
|
|
- v-for="schedule in clientGroup.schedules"
|
|
|
- :key="schedule.id"
|
|
|
- clickable
|
|
|
- @click="openScheduleDialog(schedule)"
|
|
|
- >
|
|
|
- <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
- {{ schedule.id }}
|
|
|
- </div>
|
|
|
- <q-item-section avatar>
|
|
|
- <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
- {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
- </q-badge>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section>
|
|
|
- <q-item-label>
|
|
|
- <q-icon name="event" size="xs" class="q-mr-xs" color="primary"/>
|
|
|
- <span class="gradient-diarista">
|
|
|
- {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
- </span>
|
|
|
- </q-item-label>
|
|
|
- <q-item-label caption>
|
|
|
- <q-icon name="person" size="xs" class="q-mr-xs" color="primary"/>
|
|
|
- <span class="gradient-diarista">
|
|
|
- {{ schedule.provider_name }}
|
|
|
- </span>
|
|
|
- </q-item-label>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section side>
|
|
|
- <q-item-label>
|
|
|
- {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
- </q-item-label>
|
|
|
- <q-item-label caption class="text-positive text-weight-bold">
|
|
|
- {{ formatCurrency(schedule.total_amount) }}
|
|
|
- </q-item-label>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section side>
|
|
|
- <q-icon name="chevron_right" />
|
|
|
- </q-item-section>
|
|
|
- </q-item>
|
|
|
- </q-list>
|
|
|
- </q-card-section>
|
|
|
- </q-card>
|
|
|
- </q-expansion-item>
|
|
|
-
|
|
|
- <div v-if="filteredGroupedSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
- <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
- <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
- {{ $t('schedules.empty_state') }}
|
|
|
+ <q-tab-panels v-model="scheduleType" animated>
|
|
|
+ <!-- AGENDAMENTOS (Default) -->
|
|
|
+ <q-tab-panel name="default">
|
|
|
+ <q-tabs
|
|
|
+ v-model="viewMode"
|
|
|
+ dense
|
|
|
+ class="text-grey"
|
|
|
+ active-color="secondary"
|
|
|
+ indicator-color="secondary"
|
|
|
+ align="left"
|
|
|
+ >
|
|
|
+ <q-tab name="client" :label="$t('schedules.view_as_client')" icon="person" />
|
|
|
+ <q-tab name="provider" :label="$t('schedules.view_as_provider')" icon="work" />
|
|
|
+ </q-tabs>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-tab-panels v-model="viewMode" animated>
|
|
|
+ <!-- Visão Cliente -->
|
|
|
+ <q-tab-panel name="client">
|
|
|
+ <div class="row q-col-gutter-md">
|
|
|
+ <div class="col-12">
|
|
|
+ <q-select
|
|
|
+ v-model="statusFilter"
|
|
|
+ :options="statusFilterOptions"
|
|
|
+ :label="$t('schedules.filter_by_status')"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ clearable
|
|
|
+ class="q-mb-md"
|
|
|
+ style="max-width: 300px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-12">
|
|
|
+ <q-expansion-item
|
|
|
+ v-for="clientGroup in filteredGroupedDefaultSchedules"
|
|
|
+ :key="clientGroup.client_id"
|
|
|
+ :label="clientGroup.client_name"
|
|
|
+ icon="person"
|
|
|
+ header-class="text-white gradient-diarista-bg"
|
|
|
+ expand-icon-class="text-white"
|
|
|
+ class="q-mb-md shadow-2"
|
|
|
+ default-opened
|
|
|
+ >
|
|
|
+ <q-card>
|
|
|
+ <q-card-section>
|
|
|
+ <q-list bordered separator>
|
|
|
+ <q-item
|
|
|
+ v-for="schedule in clientGroup.schedules"
|
|
|
+ :key="schedule.id"
|
|
|
+ clickable
|
|
|
+ @click="openScheduleDialog(schedule)"
|
|
|
+ >
|
|
|
+ <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
+ {{ schedule.id }}
|
|
|
+ </div>
|
|
|
+ <q-item-section avatar>
|
|
|
+ <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
+ {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
+ </q-badge>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label>
|
|
|
+ <q-icon name="event" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption>
|
|
|
+ <q-icon name="person" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.provider_name }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-item-label>
|
|
|
+ {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption class="text-positive text-weight-bold">
|
|
|
+ {{ formatCurrency(schedule.total_amount) }}
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-icon name="chevron_right" />
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
+ </q-card-section>
|
|
|
+ </q-card>
|
|
|
+ </q-expansion-item>
|
|
|
+
|
|
|
+ <div v-if="filteredGroupedDefaultSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
+ <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
+ <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
+ {{ $t('schedules.empty_state') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <!-- Visão Prestador -->
|
|
|
+ <q-tab-panel name="provider">
|
|
|
+ <div class="row q-col-gutter-md">
|
|
|
+ <div class="col-12">
|
|
|
+ <q-select
|
|
|
+ v-model="statusFilter"
|
|
|
+ :options="statusFilterOptions"
|
|
|
+ :label="$t('schedules.filter_by_status')"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ clearable
|
|
|
+ class="q-mb-md"
|
|
|
+ style="max-width: 300px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-12">
|
|
|
+ <q-expansion-item
|
|
|
+ v-for="clientGroup in filteredGroupedDefaultSchedules"
|
|
|
+ :key="clientGroup.client_id"
|
|
|
+ :label="clientGroup.client_name"
|
|
|
+ icon="person"
|
|
|
+ header-class="text-white gradient-diarista-bg"
|
|
|
+ expand-icon-class="text-white"
|
|
|
+ class="q-mb-md shadow-2 rounded-borders"
|
|
|
+ default-opened
|
|
|
+ >
|
|
|
+ <q-card>
|
|
|
+ <q-card-section>
|
|
|
+ <q-list bordered separator>
|
|
|
+ <q-item
|
|
|
+ v-for="schedule in clientGroup.schedules"
|
|
|
+ :key="schedule.id"
|
|
|
+ clickable
|
|
|
+ @click="openScheduleDialog(schedule)"
|
|
|
+ >
|
|
|
+ <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
+ {{ schedule.id }}
|
|
|
+ </div>
|
|
|
+ <q-item-section avatar>
|
|
|
+ <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
+ {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
+ </q-badge>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label>
|
|
|
+ <q-icon name="event" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption>
|
|
|
+ <q-icon name="person" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.provider_name }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-item-label>
|
|
|
+ {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption class="text-positive text-weight-bold">
|
|
|
+ {{ formatCurrency(schedule.total_amount) }}
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-icon name="chevron_right" />
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
+ </q-card-section>
|
|
|
+ </q-card>
|
|
|
+ </q-expansion-item>
|
|
|
+
|
|
|
+ <div v-if="filteredGroupedDefaultSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
+ <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
+ <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
+ {{ $t('schedules.empty_state') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </q-tab-panel>
|
|
|
+ </q-tab-panels>
|
|
|
</q-tab-panel>
|
|
|
|
|
|
- <q-tab-panel name="provider">
|
|
|
- <div class="row q-col-gutter-md">
|
|
|
- <div class="col-12">
|
|
|
- <q-select
|
|
|
- v-model="statusFilter"
|
|
|
- :options="statusFilterOptions"
|
|
|
- :label="$t('schedules.filter_by_status')"
|
|
|
- outlined
|
|
|
- dense
|
|
|
- emit-value
|
|
|
- map-options
|
|
|
- clearable
|
|
|
- class="q-mb-md"
|
|
|
- style="max-width: 300px"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="col-12">
|
|
|
- <q-expansion-item
|
|
|
- v-for="clientGroup in filteredGroupedSchedules"
|
|
|
- :key="clientGroup.client_id"
|
|
|
- :label="clientGroup.client_name"
|
|
|
- icon="person"
|
|
|
- header-class="bg-primary text-white"
|
|
|
- expand-icon-class="text-white"
|
|
|
- class="q-mb-md shadow-2 rounded-borders"
|
|
|
- default-opened
|
|
|
- >
|
|
|
- <q-card>
|
|
|
- <q-card-section>
|
|
|
- <q-list bordered separator>
|
|
|
- <q-item
|
|
|
- v-for="schedule in clientGroup.schedules"
|
|
|
- :key="schedule.id"
|
|
|
- clickable
|
|
|
- @click="openScheduleDialog(schedule)"
|
|
|
- >
|
|
|
- <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
- {{ schedule.id }}
|
|
|
- </div>
|
|
|
- <q-item-section avatar>
|
|
|
- <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
- {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
- </q-badge>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section>
|
|
|
- <q-item-label>
|
|
|
- <q-icon name="event" size="xs" class="q-mr-xs" color="primary"/>
|
|
|
- <span class="gradient-diarista">
|
|
|
- {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
- </span>
|
|
|
- </q-item-label>
|
|
|
- <q-item-label caption>
|
|
|
- <q-icon name="person" size="xs" class="q-mr-xs" color="primary"/>
|
|
|
- <span class="gradient-diarista">
|
|
|
- {{ schedule.provider_name }}
|
|
|
- </span>
|
|
|
- </q-item-label>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section side>
|
|
|
- <q-item-label>
|
|
|
- {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
- </q-item-label>
|
|
|
- <q-item-label caption class="text-positive text-weight-bold">
|
|
|
- {{ formatCurrency(schedule.total_amount) }}
|
|
|
- </q-item-label>
|
|
|
- </q-item-section>
|
|
|
-
|
|
|
- <q-item-section side>
|
|
|
- <q-icon name="chevron_right" />
|
|
|
- </q-item-section>
|
|
|
- </q-item>
|
|
|
- </q-list>
|
|
|
- </q-card-section>
|
|
|
- </q-card>
|
|
|
- </q-expansion-item>
|
|
|
-
|
|
|
- <div v-if="filteredGroupedSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
- <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
- <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
- {{ $t('schedules.empty_state') }}
|
|
|
+ <q-tab-panel name="custom">
|
|
|
+ <q-tabs
|
|
|
+ v-model="viewMode"
|
|
|
+ dense
|
|
|
+ class="text-grey"
|
|
|
+ active-color="secondary"
|
|
|
+ indicator-color="secondary"
|
|
|
+ align="left"
|
|
|
+ >
|
|
|
+ <q-tab name="client" :label="$t('schedules.view_as_client')" icon="person" />
|
|
|
+ <q-tab name="provider" :label="$t('schedules.view_as_provider')" icon="work" />
|
|
|
+ </q-tabs>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-tab-panels v-model="viewMode" animated>
|
|
|
+ <!-- Visão Cliente -->
|
|
|
+ <q-tab-panel name="client">
|
|
|
+ <div class="row q-col-gutter-md">
|
|
|
+ <div class="col-12">
|
|
|
+ <q-select
|
|
|
+ v-model="statusFilter"
|
|
|
+ :options="statusFilterOptions"
|
|
|
+ :label="$t('schedules.filter_by_status')"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ clearable
|
|
|
+ class="q-mb-md"
|
|
|
+ style="max-width: 300px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-12">
|
|
|
+ <q-expansion-item
|
|
|
+ v-for="clientGroup in filteredGroupedCustomSchedules"
|
|
|
+ :key="clientGroup.client_id"
|
|
|
+ :label="clientGroup.client_name"
|
|
|
+ icon="person"
|
|
|
+ header-class="text-white gradient-diarista-bg"
|
|
|
+ expand-icon-class="text-white"
|
|
|
+ class="q-mb-md shadow-2 rounded-borders"
|
|
|
+ default-opened
|
|
|
+ >
|
|
|
+ <q-card>
|
|
|
+ <q-card-section>
|
|
|
+ <q-list bordered separator>
|
|
|
+ <q-item
|
|
|
+ v-for="schedule in clientGroup.schedules"
|
|
|
+ :key="schedule.id"
|
|
|
+ clickable
|
|
|
+ @click="openCustomScheduleDialog(schedule)"
|
|
|
+ >
|
|
|
+ <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
+ {{ schedule.id }}
|
|
|
+ </div>
|
|
|
+ <q-item-section avatar>
|
|
|
+ <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
+ {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
+ </q-badge>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label>
|
|
|
+ <q-icon name="event" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption>
|
|
|
+ <q-icon name="person" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.provider_name || 'N/A' }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-item-label>
|
|
|
+ {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption class="text-positive text-weight-bold">
|
|
|
+ {{ formatCurrency(schedule.total_amount) }}
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-icon name="chevron_right" />
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
+ </q-card-section>
|
|
|
+ </q-card>
|
|
|
+ </q-expansion-item>
|
|
|
+
|
|
|
+ <div v-if="filteredGroupedCustomSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
+ <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
+ <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
+ {{ $t('opportunities.empty_state') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </q-tab-panel>
|
|
|
+
|
|
|
+ <!-- Visão Prestador -->
|
|
|
+ <q-tab-panel name="provider">
|
|
|
+ <div class="row q-col-gutter-md">
|
|
|
+ <div class="col-12 row q-gutter-md">
|
|
|
+ <q-select
|
|
|
+ v-model="statusFilter"
|
|
|
+ :options="statusFilterOptions"
|
|
|
+ :label="$t('schedules.filter_by_status')"
|
|
|
+ outlined
|
|
|
+ dense
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ clearable
|
|
|
+ style="max-width: 300px"
|
|
|
+ class="col-3"
|
|
|
+ />
|
|
|
+ <div class="col-3">
|
|
|
+ <ProviderSelect
|
|
|
+ v-model="selectedProvider"
|
|
|
+ :label="$t('common.terms.user')"
|
|
|
+ dense
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-12">
|
|
|
+ <q-expansion-item
|
|
|
+ v-for="clientGroup in filteredGroupedCustomSchedules"
|
|
|
+ :key="clientGroup.client_id"
|
|
|
+ :label="clientGroup.client_name"
|
|
|
+ icon="person"
|
|
|
+ header-class="text-white gradient-diarista-bg"
|
|
|
+ expand-icon-class="text-white"
|
|
|
+ class="q-mb-md shadow-2 rounded-borders"
|
|
|
+ default-opened
|
|
|
+ >
|
|
|
+ <q-card>
|
|
|
+ <q-card-section>
|
|
|
+ <q-list bordered separator>
|
|
|
+ <q-item
|
|
|
+ v-for="schedule in clientGroup.schedules"
|
|
|
+ :key="schedule.id"
|
|
|
+ clickable
|
|
|
+ @click="openCustomScheduleDialog(schedule)"
|
|
|
+ >
|
|
|
+ <div class="q-my-auto q-pr-md" style="width: 30px">
|
|
|
+ {{ schedule.id }}
|
|
|
+ </div>
|
|
|
+ <q-item-section avatar>
|
|
|
+ <q-badge :color="getStatusColor(schedule.status)" class="q-pa-sm">
|
|
|
+ {{ $t(`schedules.statuses.${schedule.status}`) }}
|
|
|
+ </q-badge>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label>
|
|
|
+ <q-icon name="event" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.date }} {{ schedule.start_time?.substring(0, 5) }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption>
|
|
|
+ <q-icon name="person" size="xs" class="q-mr-xs" color="info"/>
|
|
|
+ <span class="gradient-diarista">
|
|
|
+ {{ schedule.provider_name || 'N/A' }}
|
|
|
+ </span>
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-item-label>
|
|
|
+ {{ schedule.period_type }} {{ $t('schedules.hours') }}
|
|
|
+ </q-item-label>
|
|
|
+ <q-item-label caption class="text-positive text-weight-bold">
|
|
|
+ {{ formatCurrency(schedule.total_amount) }}
|
|
|
+ </q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+
|
|
|
+ <q-item-section side>
|
|
|
+ <q-icon name="chevron_right" />
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
+ </q-card-section>
|
|
|
+ </q-card>
|
|
|
+ </q-expansion-item>
|
|
|
+ <div v-if="selectedProvider && providerProposals.length > 0" class="q-mt-xl">
|
|
|
+ <div class="text-h6 q-mb-md">{{ $t('opportunities.my_proposals') }}</div>
|
|
|
+ <DefaultTable
|
|
|
+ :rows="providerProposals"
|
|
|
+ :columns="proposalColumns"
|
|
|
+ row-key="id"
|
|
|
+ flat
|
|
|
+ bordered
|
|
|
+ :rows-per-page-options="[10, 20, 50]"
|
|
|
+ class="sticky-header-table"
|
|
|
+ no-api-call
|
|
|
+ :api-call="() => {}"
|
|
|
+ :open-item="true"
|
|
|
+ :show-columns-select="false"
|
|
|
+ :show-search-field="false"
|
|
|
+ :add-item="false"
|
|
|
+ @on-row-click="onOpenProposal"
|
|
|
+ >
|
|
|
+ <template #body-cell-status="props">
|
|
|
+ <q-td :props="props">
|
|
|
+ <q-badge
|
|
|
+ :color="props.row.deleted_at ? 'negative' : props.row.schedule?.provider_id ? 'positive' : 'warning'"
|
|
|
+ :label="props.value"
|
|
|
+ />
|
|
|
+ </q-td>
|
|
|
+ </template>
|
|
|
+ </DefaultTable>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="filteredGroupedCustomSchedules.length === 0" class="text-center q-pa-xl">
|
|
|
+ <q-icon name="event_busy" size="64px" color="grey-5" />
|
|
|
+ <div class="text-h6 text-grey-7 q-mt-md">
|
|
|
+ {{ $t('opportunities.empty_state') }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </q-tab-panel>
|
|
|
+ </q-tab-panels>
|
|
|
</q-tab-panel>
|
|
|
</q-tab-panels>
|
|
|
</div>
|
|
|
@@ -206,21 +461,29 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { onMounted, ref, computed } from 'vue'
|
|
|
+import { onMounted, ref, computed, watch } from 'vue'
|
|
|
import { useQuasar } from 'quasar'
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
import DefaultHeaderPage from 'src/components/layout/DefaultHeaderPage.vue'
|
|
|
import ViewScheduleDialog from 'src/pages/schedule/components/ViewScheduleDialog.vue'
|
|
|
-import { getSchedulesGroupedByClient, updateScheduleStatus } from 'src/api/schedule'
|
|
|
+import ViewCustomScheduleDialog from 'src/pages/opportunity/components/ViewCustomScheduleDialog.vue'
|
|
|
+import { getSchedulesGroupedByClient, getSchedulesGroupedByClientCustom, updateScheduleStatus } from 'src/api/schedule'
|
|
|
+import { getProvidersProposalsAndOpportunities } from 'src/api/customSchedule'
|
|
|
+import ProviderSelect from 'src/components/provider/ProviderSelect.vue'
|
|
|
+import DefaultTable from 'src/components/defaults/DefaultTable.vue'
|
|
|
|
|
|
const $q = useQuasar()
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
const isLoading = ref(true)
|
|
|
+const scheduleType = ref('default')
|
|
|
const viewMode = ref('client')
|
|
|
const statusFilter = ref(null)
|
|
|
-const groupedSchedules = ref([])
|
|
|
-
|
|
|
+const groupedDefaultSchedules = ref([])
|
|
|
+const groupedCustomSchedules = ref([])
|
|
|
+const selectedProvider = ref(null)
|
|
|
+const providerProposals = ref([])
|
|
|
+const availableOpportunities = ref([])
|
|
|
const statusFilterOptions = computed(() => [
|
|
|
{ label: t('schedules.all_statuses'), value: null },
|
|
|
{ label: t('schedules.statuses.pending'), value: 'pending' },
|
|
|
@@ -232,21 +495,84 @@ const statusFilterOptions = computed(() => [
|
|
|
{ label: t('schedules.statuses.finished'), value: 'finished' }
|
|
|
])
|
|
|
|
|
|
-const filteredGroupedSchedules = computed(() => {
|
|
|
+const filteredGroupedDefaultSchedules = computed(() => {
|
|
|
if (!statusFilter.value) {
|
|
|
- return groupedSchedules.value
|
|
|
+ return groupedDefaultSchedules.value
|
|
|
}
|
|
|
|
|
|
- return groupedSchedules.value
|
|
|
+ return groupedDefaultSchedules.value
|
|
|
.map(clientGroup => ({
|
|
|
...clientGroup,
|
|
|
- schedules: clientGroup.schedules.filter(
|
|
|
+ schedules: clientGroup.schedules?.filter(
|
|
|
schedule => schedule.status === statusFilter.value
|
|
|
)
|
|
|
}))
|
|
|
- .filter(clientGroup => clientGroup.schedules.length > 0)
|
|
|
+ .filter(clientGroup => clientGroup.schedules?.length > 0)
|
|
|
})
|
|
|
|
|
|
+const filteredGroupedCustomSchedules = computed(() => {
|
|
|
+ if (viewMode.value === 'provider' && selectedProvider.value) {
|
|
|
+ const opportunities = availableOpportunities.value
|
|
|
+ if (!statusFilter.value) {
|
|
|
+ return opportunities
|
|
|
+ }
|
|
|
+ return opportunities
|
|
|
+ .map(clientGroup => ({
|
|
|
+ ...clientGroup,
|
|
|
+ schedules: clientGroup.schedules?.filter(
|
|
|
+ schedule => schedule.status === statusFilter.value
|
|
|
+ )
|
|
|
+ }))
|
|
|
+ .filter(clientGroup => clientGroup.schedules?.length > 0)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!statusFilter.value) {
|
|
|
+ return groupedCustomSchedules.value
|
|
|
+ }
|
|
|
+
|
|
|
+ return groupedCustomSchedules.value
|
|
|
+ .map(clientGroup => ({
|
|
|
+ ...clientGroup,
|
|
|
+ schedules: clientGroup.schedules?.filter(
|
|
|
+ schedule => schedule.status === statusFilter.value
|
|
|
+ )
|
|
|
+ }))
|
|
|
+ .filter(clientGroup => clientGroup.schedules?.length > 0)
|
|
|
+})
|
|
|
+
|
|
|
+const proposalColumns = computed(() => [
|
|
|
+ {
|
|
|
+ name: 'id',
|
|
|
+ label: 'ID',
|
|
|
+ field: 'schedule_id',
|
|
|
+ align: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'client',
|
|
|
+ label: t('opportunities.client'),
|
|
|
+ field: row => row.schedule?.client?.user?.name || '-',
|
|
|
+ align: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'date',
|
|
|
+ label: t('opportunities.date'),
|
|
|
+ field: row => new Date(row.schedule?.date).toLocaleDateString('pt-BR'),
|
|
|
+ align: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'period',
|
|
|
+ label: t('opportunities.period'),
|
|
|
+ field: row => row.schedule?.period === 'morning' ? t('provider_working_days.morning') : t('provider_working_days.afternoon'),
|
|
|
+ align: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: 'status',
|
|
|
+ label: t('common.terms.status'),
|
|
|
+ field: row => row.schedule?.status,
|
|
|
+ align: 'left'
|
|
|
+ }
|
|
|
+])
|
|
|
+
|
|
|
const formatCurrency = (value) => {
|
|
|
if (!value) return 'R$ 0,00'
|
|
|
return `R$ ${Number(value).toFixed(2).replace('.', ',')}`
|
|
|
@@ -268,8 +594,12 @@ const getStatusColor = (status) => {
|
|
|
const loadSchedules = async () => {
|
|
|
try {
|
|
|
isLoading.value = true
|
|
|
- const data = await getSchedulesGroupedByClient()
|
|
|
- groupedSchedules.value = data
|
|
|
+ const [defaultData, customData] = await Promise.all([
|
|
|
+ getSchedulesGroupedByClient(),
|
|
|
+ getSchedulesGroupedByClientCustom()
|
|
|
+ ])
|
|
|
+ groupedDefaultSchedules.value = defaultData
|
|
|
+ groupedCustomSchedules.value = customData
|
|
|
} catch (error) {
|
|
|
$q.notify({
|
|
|
type: 'negative',
|
|
|
@@ -281,6 +611,20 @@ const loadSchedules = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+const loadProviderProposalsAndOpportunities = async (providerId) => {
|
|
|
+ try {
|
|
|
+ const response = await getProvidersProposalsAndOpportunities(providerId)
|
|
|
+ providerProposals.value = response.proposals
|
|
|
+ availableOpportunities.value = response.opportunities
|
|
|
+ } catch (error) {
|
|
|
+ $q.notify({
|
|
|
+ type: 'negative',
|
|
|
+ message: error.message || t('common.ui.messages.error_loading_data'),
|
|
|
+ position: 'top'
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const openScheduleDialog = (schedule) => {
|
|
|
$q.dialog({
|
|
|
component: ViewScheduleDialog,
|
|
|
@@ -293,6 +637,29 @@ const openScheduleDialog = (schedule) => {
|
|
|
onCancel: handleCancel
|
|
|
},
|
|
|
persistent: true
|
|
|
+ }).onOk(async () => {
|
|
|
+ await loadSchedules();
|
|
|
+ if (selectedProvider.value?.value) {
|
|
|
+ await loadProviderProposalsAndOpportunities(selectedProvider.value.value);
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const openCustomScheduleDialog = (schedule) => {
|
|
|
+ $q.dialog({
|
|
|
+ component: ViewCustomScheduleDialog,
|
|
|
+ componentProps: {
|
|
|
+ schedule,
|
|
|
+ viewMode: viewMode.value,
|
|
|
+ providerId: selectedProvider.value?.value || null,
|
|
|
+ onMarkAsPaid: handleMarkAsPaid,
|
|
|
+ onRefreshData: async () => {
|
|
|
+ await loadSchedules();
|
|
|
+ if (selectedProvider.value?.value) {
|
|
|
+ await loadProviderProposalsAndOpportunities(selectedProvider.value.value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -325,13 +692,23 @@ const handleCancel = async (scheduleId) => {
|
|
|
await updateStatus(scheduleId, 'cancelled')
|
|
|
}
|
|
|
|
|
|
+const onOpenProposal = ({row}) => {
|
|
|
+ openCustomScheduleDialog(row.schedule)
|
|
|
+}
|
|
|
+
|
|
|
+watch(selectedProvider, async (newProvider) => {
|
|
|
+ if (newProvider?.value && viewMode.value === 'provider') {
|
|
|
+ await loadProviderProposalsAndOpportunities(newProvider.value);
|
|
|
+ } else {
|
|
|
+ providerProposals.value = []
|
|
|
+ availableOpportunities.value = []
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
onMounted(async () => {
|
|
|
await loadSchedules()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.gap {
|
|
|
- gap: 16px;
|
|
|
-}
|
|
|
</style>
|