407 lines
8.8 KiB
Vue
407 lines
8.8 KiB
Vue
<template>
|
|
<div class="videos-page">
|
|
<section class="app-page__header">
|
|
<div>
|
|
<h2 class="app-page__title">Biblioteca de vídeos</h2>
|
|
<p class="app-page__subtitle">
|
|
Filtre os vídeos processados pela IA, acompanhe o status dos recortes e inicie novas captações
|
|
em poucos cliques.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="app-actions-bar">
|
|
<Button
|
|
variant="ghost"
|
|
icon="sym_o_refresh"
|
|
label="Atualizar"
|
|
@click="handleSearch(pagination)"
|
|
/>
|
|
<Button icon="sym_o_add" label="Novo vídeo" @click="handleAddVideo" />
|
|
</div>
|
|
</section>
|
|
|
|
<q-card flat bordered class="videos-page__filters app-card">
|
|
<div class="videos-page__filters-head">
|
|
<div>
|
|
<h3>Filtrar resultados</h3>
|
|
<span>Refine a busca por título, identificador ou situação.</span>
|
|
</div>
|
|
|
|
<div class="videos-page__filters-actions">
|
|
<Button
|
|
variant="ghost"
|
|
color="secondary"
|
|
text-color="primary"
|
|
icon="sym_o_close"
|
|
label="Limpar filtros"
|
|
:disabled="!hasFilters"
|
|
@click="resetFilters"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="videos-page__filters-grid">
|
|
<TextField
|
|
v-model="filters.title"
|
|
label="Título"
|
|
placeholder="Ex.: Cortes da live com convidados"
|
|
>
|
|
<template #prepend>
|
|
<q-icon name="sym_o_title" color="primary" />
|
|
</template>
|
|
</TextField>
|
|
|
|
<TextField
|
|
v-model="filters.videoId"
|
|
label="Video ID"
|
|
placeholder="Ex.: x9-YRAYhesI"
|
|
>
|
|
<template #prepend>
|
|
<q-icon name="sym_o_confirmation_number" color="primary" />
|
|
</template>
|
|
</TextField>
|
|
|
|
<Dropdown
|
|
v-model="filters.situations"
|
|
label="Situação"
|
|
clearable
|
|
:options="situations"
|
|
:loading="situationsLoading"
|
|
multiple
|
|
emit-value
|
|
map-options
|
|
/>
|
|
</div>
|
|
|
|
<div class="videos-page__filters-footer">
|
|
<Button
|
|
label="Buscar vídeos"
|
|
icon="sym_o_search"
|
|
:loading="loading"
|
|
:disabled="loading"
|
|
@click="handleSearch({ ...pagination, page: 1 })"
|
|
/>
|
|
</div>
|
|
</q-card>
|
|
|
|
<Table
|
|
key="videos-table"
|
|
class="videos-page__table"
|
|
title="Resultados"
|
|
subtitle="Veja os vídeos cadastrados, a quantidade de clipes e o status de processamento."
|
|
:columns="columns"
|
|
:rows="rows"
|
|
:pagination="pagination"
|
|
:loading="loading"
|
|
@update:pagination="updatePagination"
|
|
>
|
|
<template #actions>
|
|
<Button
|
|
variant="ghost"
|
|
icon="sym_o_download"
|
|
label="Exportar CSV"
|
|
@click="handleExport"
|
|
/>
|
|
</template>
|
|
|
|
<template #empty-message>
|
|
Nenhum vídeo encontrado para os filtros selecionados.
|
|
</template>
|
|
|
|
<template #body="props">
|
|
<q-tr :props="props">
|
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
|
<div class="videos-page__cell">
|
|
<div class="videos-page__cell-label">{{ col.label }}</div>
|
|
<div class="videos-page__cell-value">{{ col.value }}</div>
|
|
</div>
|
|
</q-td>
|
|
</q-tr>
|
|
</template>
|
|
</Table>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Button from "@components/Button";
|
|
import Table from "@components/Table";
|
|
import TextField from "@components/TextField";
|
|
import Dropdown from "@components/Dropdown";
|
|
|
|
import { API } from "@config/axios";
|
|
import { getErrorMessage } from "@utils/axios";
|
|
|
|
const columns = [
|
|
{
|
|
name: "id",
|
|
label: "Identificador",
|
|
align: "left",
|
|
field: "id",
|
|
},
|
|
{
|
|
name: "title",
|
|
label: "Título",
|
|
align: "left",
|
|
field: "title",
|
|
},
|
|
{
|
|
name: "clips_quantity",
|
|
label: "Clipes",
|
|
align: "center",
|
|
field: "clips_quantity",
|
|
},
|
|
{
|
|
name: "videoid",
|
|
label: "Video ID",
|
|
field: "videoid",
|
|
},
|
|
{
|
|
name: "situation",
|
|
label: "Situação",
|
|
field: "situation",
|
|
},
|
|
];
|
|
|
|
export default {
|
|
name: "VideosList",
|
|
components: {
|
|
Button,
|
|
Table,
|
|
TextField,
|
|
Dropdown,
|
|
},
|
|
data() {
|
|
return {
|
|
columns,
|
|
rows: [],
|
|
loading: false,
|
|
pagination: {
|
|
page: 1,
|
|
direction: "desc",
|
|
perPage: 10,
|
|
total: 0,
|
|
totalPages: 1,
|
|
hasNext: false,
|
|
hasPrev: false,
|
|
},
|
|
situations: [],
|
|
situationsLoading: false,
|
|
filters: {
|
|
title: "",
|
|
videoId: "",
|
|
situations: [],
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
hasFilters() {
|
|
return (
|
|
!!this.filters.title ||
|
|
!!this.filters.videoId ||
|
|
(Array.isArray(this.filters.situations) && this.filters.situations.length > 0)
|
|
);
|
|
},
|
|
},
|
|
created() {
|
|
this.initializePage();
|
|
},
|
|
methods: {
|
|
async initializePage() {
|
|
await this.getSituation();
|
|
await this.handleSearch(this.pagination);
|
|
},
|
|
async handleSearch(pagination) {
|
|
try {
|
|
this.loading = true;
|
|
|
|
const baseParams = {};
|
|
|
|
if (this.filters.title) {
|
|
baseParams.title = this.filters.title;
|
|
}
|
|
|
|
if (this.filters.videoId) {
|
|
baseParams.videoId = this.filters.videoId;
|
|
}
|
|
|
|
if (Array.isArray(this.filters.situations) && this.filters.situations.length) {
|
|
baseParams.situation = this.filters.situations;
|
|
}
|
|
|
|
const { data } = await API.get("/videos", {
|
|
params: {
|
|
perPage: pagination.perPage,
|
|
page: pagination.page,
|
|
...baseParams,
|
|
},
|
|
paramsSerializer: {
|
|
indexes: null,
|
|
},
|
|
});
|
|
|
|
this.rows = data.content;
|
|
this.pagination = data.pagination;
|
|
} catch (error) {
|
|
this.$q.notify({
|
|
type: "negative",
|
|
message: getErrorMessage(error, "Erro ao buscar vídeos"),
|
|
});
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
updatePagination(pagination) {
|
|
this.handleSearch(pagination);
|
|
},
|
|
async getSituation() {
|
|
try {
|
|
this.situationsLoading = true;
|
|
|
|
const { data } = await API.get("/videos/situacoes");
|
|
|
|
this.situations = data;
|
|
} catch (error) {
|
|
this.$q.notify({
|
|
type: "negative",
|
|
message: getErrorMessage(error, "Erro ao buscar situações"),
|
|
});
|
|
} finally {
|
|
this.situationsLoading = false;
|
|
}
|
|
},
|
|
handleAddVideo() {
|
|
this.$router.push("/videos/new");
|
|
},
|
|
resetFilters() {
|
|
this.filters = {
|
|
title: "",
|
|
videoId: "",
|
|
situations: [],
|
|
};
|
|
|
|
this.handleSearch({ ...this.pagination, page: 1 });
|
|
},
|
|
handleExport() {
|
|
if (!this.rows.length) {
|
|
this.$q.notify({
|
|
type: "warning",
|
|
message: "Não há dados para exportar. Ajuste os filtros e tente novamente.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
this.$q.notify({
|
|
type: "info",
|
|
message: "Exportação em CSV disponível em breve.",
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.videos-page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 32px;
|
|
}
|
|
|
|
.videos-page__filters h3 {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
color: #101828;
|
|
}
|
|
|
|
.videos-page__filters span {
|
|
color: #475467;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.videos-page__filters-head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.videos-page__filters-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.videos-page__filters-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.videos-page__filters-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 24px;
|
|
}
|
|
|
|
.videos-page__table {
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
.videos-page__cell {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.videos-page__cell-label {
|
|
display: none;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #98a2b3;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.videos-page__cell-value {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: #101828;
|
|
}
|
|
|
|
body.body--dark .videos-page__filters h3 {
|
|
color: rgba(255, 255, 255, 0.92);
|
|
}
|
|
|
|
body.body--dark .videos-page__filters span {
|
|
color: rgba(255, 255, 255, 0.7);
|
|
}
|
|
|
|
body.body--dark .videos-page__cell-value {
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.videos-page__filters-head {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.app-actions-bar {
|
|
width: 100%;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.videos-page__filters-footer {
|
|
justify-content: stretch;
|
|
}
|
|
|
|
.videos-page__filters-footer > * {
|
|
width: 100%;
|
|
}
|
|
|
|
.videos-page__cell-label {
|
|
display: block;
|
|
}
|
|
}
|
|
</style>
|