diff --git a/.gitignore b/.gitignore index 9b6465f..2741f47 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ node_modules .local .env msg.md +conversation.md diff --git a/.lefthook.yml b/.lefthook.yml deleted file mode 100644 index de23924..0000000 --- a/.lefthook.yml +++ /dev/null @@ -1,7 +0,0 @@ -pre-push:
- parallel: true
- commands:
- rubocop:
-
run: bundle exec rubocop - brakeman:
-
run: bin/brakeman --no-pager
diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/Gemfile b/Gemfile index 47f42e7..8e468e9 100644 --- a/Gemfile +++ b/Gemfile @@ -52,9 +52,6 @@ end group :development do
Use console on exceptions pages [https://github.com/rails/web-console]
- gem "lefthook", require: false end
group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 84cff11..a326b00 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,7 +141,6 @@ GEM thor (~> 1.3) zeitwerk (>= 2.6.18, < 3.0) language_server-protocol (3.17.0.5)
- lefthook (1.12.3) lint_roller (1.1.0) logger (1.7.0) loofah (2.24.1) @@ -375,7 +374,6 @@ DEPENDENCIES inertia_rails (~> 3.11) jbuilder kamal
- lefthook
mysql2 (
> 0.5) pagy (> 9.3) puma (>= 5.0) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3aed7a4..71dc3bc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -62,7 +62,7 @@ class ApplicationController < ActionController::Base }, { description: "Mostras", -
path: ""
-
path: mostras_path }, { description: "Cinemas",
@@ -98,7 +98,7 @@ class ApplicationController < ActionController::Base "Notícias": [ { description: "Todas as notícias",
-
path: noticias_url
-
path: noticias_path } ], "Mídias": [
diff --git a/app/controllers/mostras_controller.rb b/app/controllers/mostras_controller.rb new file mode 100644 index 0000000..e81c7df --- /dev/null +++ b/app/controllers/mostras_controller.rb @@ -0,0 +1,22 @@ +class MostrasController < ApplicationController
- def index
- @edicao_atual = Edicao.find_by(descricao: "2024") # TODO: Change to 2025
- @mostras = @edicao_atual.mostras.group_by(&:display_name)
- @categorias = @mostras.map { |categoria, mostras| { name: categoria, class: mostras.first.tag_class } }
- items = [
-
# Links tem que vir do controller para incluir localizacao. Textos também -
{ name: "Todos os Filmes", route: "/", icon: "program" }, -
{ name: "Mostras", route: mostras_url, icon: "user" }, -
{ name: "Cinemas", route: "/", icon: "change" }, -
{ name: "Juri", route: "/", icon: "clock" }, -
{ name: "Equipe", route: "/", icon: "clock" } - ]
- render inertia: "Mostras/Index", props: {
-
rootUrl: @root_url, -
items: items, -
categorias: @categorias.as_json - }
- end +end diff --git a/app/controllers/programs_controller.rb b/app/controllers/programs_controller.rb index 9bb3a02..6642f62 100644 --- a/app/controllers/programs_controller.rb +++ b/app/controllers/programs_controller.rb @@ -21,41 +21,72 @@ class ProgramsController < ApplicationController base_scope = Programacao .joins(pelicula: :mostra) .includes(:cinema, pelicula: :mostra)
-
.where(importacoesprog_id: last_import_id) # ALTERAR PARA EDICAO ID
-
.where(importacoesprog_id: last_import_id, edicao_id: EDICAO_ATUAL) # TODO: remover importacoes? -
set_filter_options(base_scope)
if params[:query].present? term = "%#{params[:query].downcase}%"
-
base_scope = base_scope.where( -
"LOWER(peliculas.titulo_ingles_coord_int) LIKE :query OR -
LOWER(peliculas.titulo_original_coord_int) LIKE :query OR -
LOWER(peliculas.titulo_portugues_coord_int) LIKE :query OR -
LOWER(peliculas.titulo_ingles_semartigo) LIKE :query OR -
LOWER(peliculas.titulo_portugues_semartigo) LIKE :query", -
query: term -
) -
selected_filters[:query] = params[:quer]
-
pelicula_ids = Pelicula.where(edicao_id: EDICAO_ATUAL).where( -
"LOWER(titulo_ingles_coord_int) LIKE :term OR -
LOWER(titulo_original_coord_int) LIKE :term OR -
LOWER(titulo_portugues_coord_int) LIKE :term OR -
LOWER(titulo_ingles_semartigo) LIKE :term OR -
LOWER(titulo_portugues_semartigo) LIKE :term", -
term: term -
).pluck(:id) -
base_scope = base_scope.where(pelicula_id: pelicula_ids) -
endselected_filters[:query] = params[:query]
- mostras_filter = Mostra.where(edicao_id: 12).to_a.uniq { |m| m.id }.sort_by { _1.permalink_pt }.as_json(
-
only: %i[id permalink_pt nome_abreviado], -
methods: [ :tag_class, :display_name ] - )
- if params[:mostrasFilter].present?
-
selected_mostra = mostras_filter.find { |c| c["permalink_pt"] == params[:mostrasFilter] }
-
endselected_mostra = @mostras_filter.find { |c| c["permalink_pt"] == params[:mostrasFilter] } selected_filters[:mostrasFilter] = selected_mostra if selected_mostra if selected_mostra base_scope = base_scope.where(mostras: { permalink_pt: selected_filters[:mostrasFilter]["permalink_pt"] }) end
-
if params[:cinemasFilter]
-
selected_cinema = @cinemas_filter.find do |cinema_filter| -
(cinema_filter["id"].to_s === params[:cinemasFilter]) && (cinema_filter["edicao_id"] == EDICAO_ATUAL) -
end -
if selected_cinema -
selected_filters[:cinemasFilter] = selected_cinema -
base_scope = base_scope.where(cinema_id: selected_cinema["id"]) -
end -
end
-
if params[:paisesFilter]
-
selected_pais = @paises_filter.find do |pais_filter| -
(pais_filter["id"].to_s === params[:paisesFilter]) -
end -
if selected_pais -
selected_filters[:paisesFilter] = selected_pais -
# raise -
# base_scope = base_scope.where(paises_id: selected_pais["id"]) -
base_scope = base_scope.joins(pelicula: :paises).where(pelicula: { paises: { id: selected_pais["id"] } }) -
end -
end
-
if params[:sessao].present?
-
# raise -
selected_sessao = @sessoes.find { |sessao| (sessao["filter_value"] === params[:sessao]) } -
if selected_sessao -
selected_filters[:sessao] = selected_sessao -
base_scope = base_scope.where(sessao: params[:sessao]..) -
end -
end
-
available_dates = base_scope.distinct.pluck(:data).sort selected_date = available_dates.first
-
if params[:date].present? parsed_date = Date.parse(params[:date]) rescue nil if parsed_date&.in?(available_dates) @@ -70,18 +101,19 @@ class ProgramsController < ApplicationController
@pagy, @programacoes = pagy_infinite(programacoes_for_date, current_page)
- @programacoes = @programacoes.map do |p|
- @programacoes = @programacoes.map do |programacao| {
-
id: p.id, -
data: p.data, -
sessao: [ p.display_sessao ], -
cinema: p.cinema&.nome, -
titulo: p.pelicula&.titulo_portugues_coord_int, -
duracao: p.pelicula&.duracao_coord_int, -
imagem: p.pelicula&.imagem, -
genero: p.pelicula&.genre, -
mostra: p.pelicula&.mostra&.display_name, -
mostra_tag_class: p.pelicula&.mostra&.tag_class
-
id: programacao.id, -
data: programacao.data, -
sessao: [ programacao.display_sessao ], -
cinema: programacao.cinema&.nome, -
titulo: programacao.pelicula&.titulo_portugues_coord_int, -
duracao: programacao.pelicula&.duracao_coord_int, -
imagem: programacao.pelicula&.imagem, -
genero: programacao.pelicula&.genre, -
paises: programacao.pelicula&.display_paises, -
mostra: programacao.pelicula&.mostra&.display_name, -
endmostra_tag_class: programacao.pelicula&.mostra&.tag_class }
@@ -95,18 +127,24 @@ class ProgramsController < ApplicationController end
# Build breadcrumbs
- render inertia: "ProgramPage", props: { rootUrl: @root_url, tabBaseUrl: program_url, items:, elements: @programacoes, pagy: @pagy,
-
mostrasFilter: mostras_filter,
-
mostrasFilter: @mostras_filter, -
cinemasFilter: @cinemas_filter, -
paisesFilter: @paises_filter, -
sessoes: @sessoes, menuTabs: @menu_tabs,
-
current_filters: {
-
current_filters: { # those are the ones used as modelValue query: params[:query],
-
mostrasFilter: selected_mostra
-
mostrasFilter: selected_mostra, -
cinemasFilter: selected_cinema, -
paisesFilter: selected_pais, -
sessao: selected_sessao }, has_active_filters: params.permit(:query, :mostrasFilter).to_h.values.any?(&:present?), crumbs: breadcrumbs(
@@ -152,9 +190,43 @@ class ProgramsController < ApplicationController def build_tab_url(date, filters) query_params = {} query_params[:mostrasFilter]= filters[:mostrasFilter]["permalink_pt"] if filters[:mostrasFilter].present?
- query_params[:cinemasFilter]= filters[:cinemasFilter]["id"] if filters[:cinemasFilter].present?
- query_params[:paisesFilter]= filters[:paisesFilter]["id"] if filters[:paisesFilter].present? query_params[:date] = date
- url_for(params: query_params, only_path: true) end
- def set_filter_options(base_scope)
- @paises_filter = base_scope.includes(pelicula: :paises)
-
.map { _1.pelicula.paises } -
.flatten -
.uniq -
.sort_by { |it| it.nome_pais } -
.as_json( -
only: %i[id nome_pais], -
methods: %i[filter_display filter_value] -
) - @mostras_filter = Mostra.where(edicao_id: EDICAO_ATUAL)
-
.to_a -
.uniq { |m| m.id } -
.sort_by { |it| it.permalink_pt } -
.as_json( -
only: %i[id permalink_pt nome_abreviado], -
methods: [ :tag_class, :display_name, :filter_value, :filter_display] -
) - @cinemas_filter = Cinema.where(edicao_id: EDICAO_ATUAL)
-
.to_a -
.uniq { |m| m.id } -
.sort_by { |it| it.nome } -
.as_json( -
only: %i[id nome endereco edicao_id], -
methods: %i[filter_display filter_value] -
) - @sessoes = Programacao.where(edicao_id: EDICAO_ATUAL).to_a.uniq { |p| p.sessao }.sort.as_json(
-
only: %i[sessao], -
methods: %i[display_sessao filter_value filter_display] - )
- end end diff --git a/app/frontend/assets/css/safelist.tailwind.txt b/app/frontend/assets/css/safelist.tailwind.txt new file mode 100644 index 0000000..325fecb --- /dev/null +++ b/app/frontend/assets/css/safelist.tailwind.txt @@ -0,0 +1,12 @@ +border-tag-premiere-brasil +border-tag-premiere-latina +border-tag-gala-abertura +border-tag-gala-encerramento +border-tag-resistencias +border-tag-cine-memoria +border-tag-midnight-movies +border-tag-cinema-capacete +border-tag-classicos-cults +border-tag-expectativas +border-tag-itinerarios-unicos +border-tag-panorama-mundial diff --git a/app/frontend/assets/css/tailwind-config.css b/app/frontend/assets/css/tailwind-config.css index eb00df2..68f99b5 100644 --- a/app/frontend/assets/css/tailwind-config.css +++ b/app/frontend/assets/css/tailwind-config.css @@ -44,13 +44,13 @@ --color-laranja-400: #FFB46A; --color-laranja-600: #FF7F00; --color-laranja-800: #C06103;
- --color-laranja-1000: #8A480;
-
--color-laranja-1000: #8A4805;
--color-magenta-200: #FFD4E9; --color-magenta-400: #FF6FB7; --color-magenta-600: #FF007F; --color-magenta-800: #B20059;
- --color-magenta-1000: #6A003;
-
--color-magenta-1000: #6A0035;
--color-vermelho-100: #FFCCCC; --color-vermelho-200: #FF7E7E; @@ -58,24 +58,24 @@ --color-vermelho-600: #FF0000; --color-vermelho-1000: #5E0000;
- --color-violeta-100: #da9fe0;
- --color-violeta-100: #DA9FE0; --color-violeta-200: #DA9FE0; --color-violeta-400: #BF68C8; --color-violeta-600: #7F008C; --color-violeta-800: #580161;
- --color-violeta-1000: #2D003;
-
--color-violeta-1000: #2D0032;
--color-amarelo-200: #FFF8DF; --color-amarelo-400: #FFECAB; --color-amarelo-600: #FFDB5C; --color-amarelo-800: #FFC700;
- --color-amarelo-1000: #D4A50;
-
--color-amarelo-1000: #D4A500;
--color-verde-200: #CAFFCF; --color-verde-400: #9EF4A6; --color-verde-600: #1BBC2B; --color-verde-800: #11861D;
- --color-verde-1000: #094A0;
-
--color-verde-1000: #094A0F;
--color-azul-400: #94BBE2; --color-azul-600: #1881F1; @@ -140,3 +140,18 @@ --breakpoint-2xl: 1440px; --breakpoint-3xl: 1920px; }
+@theme static {
- --color-tag-premiere-brasil: var(--color-laranja-600);
- --color-tag-premiere-latina: var(--color-amarelo-800);
- --color-tag-gala-abertura: var(--color-magenta-600);
- --color-tag-gala-encerramento: var(--color-magenta-800);
- --color-tag-resistencias: var(--color-neutrals-200);
- --color-tag-cine-memoria: var(--color-violeta-100);
- --color-tag-midnight-movies: var(--color-violeta-600);
- --color-tag-cinema-capacete: var(--color-neutrals-400);
- --color-tag-classicos-cults: var(--color-neutrals-900);
- --color-tag-expectativas: var(--color-azul-600);
- --color-tag-itinerarios-unicos: var(--color-verde-600);
- --color-tag-panorama-mundial: var(--color-vermelho-600); +} diff --git a/app/frontend/assets/index.css b/app/frontend/assets/index.css index 9023c45..4fd5592 100644 --- a/app/frontend/assets/index.css +++ b/app/frontend/assets/index.css @@ -1,4 +1,3 @@ -@import "./css/tailwind-config.css"; @import "./css/typography.css"; @import "./css/animation.css"; @import "./css/tag_base.css"; diff --git a/app/frontend/components/common/cards/SessionCard.vue b/app/frontend/components/common/cards/SessionCard.vue index 93a15d3..abb88ed 100644 --- a/app/frontend/components/common/cards/SessionCard.vue +++ b/app/frontend/components/common/cards/SessionCard.vue @@ -64,9 +64,7 @@ const movieGenre = computed(() => { {{ props.session.titulo }}
-
<span class="text-overline text-on-dark-secondary">{{ -
"TBD" -
}}</span>
-
<span class="text-overline text-on-dark-secondary">{{ props.session.paises }}</span> <img src="@assets/icons/divisor.svg" alt="divisor"
diff --git a/app/frontend/components/common/icons/index.js b/app/frontend/components/common/icons/index.js index e267dfa..7c5a4eb 100644 --- a/app/frontend/components/common/icons/index.js +++ b/app/frontend/components/common/icons/index.js @@ -9,7 +9,7 @@ export { default as IconSearch } from "./actions/IconSearch.vue"; // Navigation export { default as IconCarretUp } from "./navigation/IconCarretUp.vue"; export { default as IconHome } from "./navigation/IconHome.vue"; -// export { default as IconChevronLeft } from "./navigation/IconChevronLeft.vue"; +export { default as IconChevronLeft } from "./navigation/IconChevronLeft.vue"; export { default as IconChevronRight } from "./navigation/IconChevronRight.vue"; // export { default as IconMenu } from "./navigation/IconMenu.vue";
diff --git a/app/frontend/components/common/icons/navigation/IconChevronLeft.vue b/app/frontend/components/common/icons/navigation/IconChevronLeft.vue new file mode 100644 index 0000000..2c21cef --- /dev/null +++ b/app/frontend/components/common/icons/navigation/IconChevronLeft.vue @@ -0,0 +1,25 @@ +<script setup> +import { BaseIcon } from "@components/common/icons" + +const props = defineProps({
- color: { type: String, default: undefined },
- active: { type: Boolean, default: false },
- width: { type: String, default: "20" },
- height: { type: String, default: "20" } +}); +</script>
+
- <template #default="{ fill }">
-
<path -
fill-rule="evenodd" -
clip-rule="evenodd" -
d="M14.3616 2.0574C14.4198 2.11545 14.466 2.18442 14.4975 2.26035C14.529 2.33629 14.5452 2.41769 14.5452 2.4999C14.5452 2.58211 14.529 2.66351 14.4975 2.73944C14.466 2.81537 14.4198 2.88434 14.3616 2.9424L7.30284 9.9999L14.3616 17.0574C14.4789 17.1748 14.5449 17.3339 14.5449 17.4999C14.5449 17.6659 14.4789 17.825 14.3616 17.9424C14.2442 18.0598 14.0851 18.1257 13.9191 18.1257C13.7531 18.1257 13.5939 18.0598 13.4766 17.9424L5.97659 10.4424C5.91838 10.3843 5.8722 10.3154 5.84069 10.2394C5.80919 10.1635 5.79297 10.0821 5.79297 9.9999C5.79297 9.91769 5.80919 9.83629 5.84069 9.76035C5.8722 9.68442 5.91838 9.61545 5.97659 9.5574L13.4766 2.0574C13.5346 1.99919 13.6036 1.95301 13.6795 1.92151C13.7555 1.89 13.8369 1.87378 13.9191 1.87378C14.0013 1.87378 14.0827 1.89 14.1586 1.92151C14.2346 1.95301 14.3035 1.99919 14.3616 2.0574Z" -
:fill="fill" -
/>
+ + +<style scoped></style> diff --git a/app/frontend/components/features/filters/ProgramsFilterForm.vue b/app/frontend/components/features/filters/ProgramsFilterForm.vue index 35b765f..4596b3b 100644 --- a/app/frontend/components/features/filters/ProgramsFilterForm.vue +++ b/app/frontend/components/features/filters/ProgramsFilterForm.vue @@ -3,11 +3,15 @@ import { computed } from "vue"; import AccordionGroup from "@/components/AccordionGroup.vue"; import ComboboxComponent from "@/components/ui/ComboboxComponent.vue"; import SearchBar from "@/components/features/filters/SearchBar.vue"; +import SelectComponent from "@/components/ui/SelectComponent.vue";
const props = defineProps({ modelValue: { type: Object, required: true }, updateField: { type: Function, required: true }, mostrasFilter: { type: Array, default: () => [] }, // Program-specific prop
- cinemasFilter: { type: Array, default: () => [] }, // Program-specific prop
- paisesFilter: { type: Array, default: () => [] },
- sessoes: { type: Array, default: () => [] }, });
// Transform cadernos prop for ComboboxComponent format @@ -17,10 +21,41 @@ const mostrasFilterOptions = computed(() => { value: caderno.permalink_pt, })); }); +// Transform cinema prop for ComboboxComponent format +const cinemasFilterOptions = computed(() => {
- return props.cinemasFilter.map(cinema => ({
- label: cinema.nome,
- value: cinema.id,
- })); +}); +// Transform cinema prop for ComboboxComponent format +const paisesFilterOptions = computed(() => {
- return props.paisesFilter.map(pais => ({
- label: pais.nome_pais,
- value: pais.id,
- })); +}); +// Transform cinema prop for ComboboxComponent format +const sessoesFilterOptions = computed(() => {
- return props.sessoes.map(sessao => ({
- label:
Início às ${sessao.filter_display}, - value: sessao.filter_value,
- })); +});
-const getMostraObjectFromTagClas = (permalink_pt) => {
- return props.mostrasFilter.find(c => c.permalink_pt === permalink_pt) || null; +const getMostraObjectFromTagClas = (filter_value) => {
- return props.mostrasFilter.find(c => c.filter_value === filter_value) || null; +}; +const getSessaoObject = (filter_value) => {
- return props.sessoes.find(c => c.filter_value === filter_value) || null; }; +const getCinemaObject = (filter_value) => {
- return props.cinemasFilter.find(c => c.filter_value === filter_value) || null; +};
+const getPaisObject = (filter_value) => {
- return props.paisesFilter.find(c => c.filter_value === filter_value) || null; +}
- <AccordionGroup
- text="Categoria"
- :isOpen="props.modelValue.categoria != null"
- text="Pais"
- :isOpen="!!props.modelValue.paisesFilter"
<template v-slot:content>
-
<div class="pt-400 overflow-hidden">
-
<div class="overflow-hidden w-full"> <ComboboxComponent
-
:collection="categorias" -
:modelValue="props.modelValue.categoria" -
@update:modelValue="(val) => props.updateField('categoria', val)"
-
:collection="paisesFilterOptions" -
:modelValue="props.modelValue.paisesFilter?.filter_value || null" -
@update:modelValue="(val) => props.updateField('paisesFilter', getPaisObject(val))" /> </div>
- -->
+@source "../../views//*.{erb,html,haml,slim}"; +@source "../../helpers//.rb"; +@source "../**/.{js,ts,jsx,tsx,vue}"; +@source "../**/*.{erb,html,rb}"; + +@source "../assets/css/safelist.tailwind.txt"; + +@import "tw-animate-css"; +@import "../assets/css/tailwind-config.css";
@custom-variant dark (&:is(.dark *));
@@ -28,7 +36,7 @@ --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive);
- --color-border: var(--color-amarelo-800);
- --color-border: var(--color-neutrals-300); --color-input: var(--input); --color-ring: var(--ring); --color-chart-1: var(--chart-1); diff --git a/app/frontend/pages/Mostras/Index.vue b/app/frontend/pages/Mostras/Index.vue new file mode 100644 index 0000000..f915078 --- /dev/null +++ b/app/frontend/pages/Mostras/Index.vue @@ -0,0 +1,56 @@ +<script setup> +import { IconProgram, IconClock, IconChange, IconNewUser } from "@components/common/icons"; +import MenuContext from "@/components/layout/navbar/MenuContext.vue"; +import TwContainer from "@/components/layout/TwContainer.vue";
+const props = defineProps({
- items: { type: Array, required: true },
- categorias: { type: Array, default: () => []} +})
+// TODO: alterar icones +const iconMapping = {
- "program": IconProgram,
- "user": IconNewUser,
- "change": IconChange,
- "clock": IconClock +}; +</script>
+
- <MenuContext
- :items="props.items"
- :icon-mapping="iconMapping"
- />
-
-
-
<li v-for="categoria in props.categorias" :class="`border-s-30 border-${categoria.class} rounded-lg overflow-hidden lg:h-[320px] h-[206px]`"> -
<figure class="relative m-0 h-full p-600 flex flex-col justify-end"> -
<img -
class="absolute inset-0 w-full h-full object-cover -z-1" -
src="https://s3.amazonaws.com/festivaldorio/2024/site/peliculas/original/mals_f01cor_2024113147.jpg" -
:alt="categoria.name" -
> -
<h1 class="text-white-transp-1000 mt-max text-header-regular-md z-1">{{ categoria.name }}</h1> -
</figure> -
</li>
-
+
+ +<style scoped> +figure::after {- position: absolute;
- content: "";
- height: 100%;
- width: 100%;
- top: 0;
- left: 0;
- background: linear-gradient(180deg, rgba(0, 0, 0, 0.30) 73.08%, rgba(0, 0, 0, 0.64) 100%);
- } +</style> diff --git a/app/frontend/pages/ProgramPage.vue b/app/frontend/pages/ProgramPage.vue index b3bfbbc..c4ab1a4 100644 --- a/app/frontend/pages/ProgramPage.vue +++ b/app/frontend/pages/ProgramPage.vue @@ -16,6 +16,7 @@ import MenuTabs from "@/components/layout/navbar/MenuTabs.vue"; import MobileTrigger from "@/components/features/filters/MobileTrigger.vue"; import ProgramsFilterForm from "@/components/features/filters/ProgramsFilterForm.vue"; import SessionCard from "@/components/common/cards/SessionCard.vue"; +import TagFilter from "@/components/common/tags/TagFilter.vue";
import { useMobileTrigger } from "@/components/features/filters/composables/useMobileTrigger"; import { useStickyMenuTabs } from "@/components/layout/navbar/composables/useStickyMenuTabs"; @@ -31,6 +32,9 @@ const props = defineProps({ ,elements: { type: Object, required: true } ,pagy: { type: Object, required: true } ,mostrasFilter: { type: Array, default: () => [] }
-
,cinemasFilter: { type: Array, default: () => [] }
-
,paisesFilter: { type: Array, default: () => [] }
-
,sessoes: { type: Array, default: () => [] } // NEW LIFE ,menuTabs: { type: Array, required: true } ,current_filters: { type: Object, default: () => ({}) } @@ -60,10 +64,18 @@ const filterSearch = (filtersFromChild) => { const cleanedFilters = {}
for (const [key, value] of Object.entries(filtersFromChild)) {
- if (key === 'mostrasFilter' && typeof value === 'object' && value?.permalink_pt) {
-
cleanedFilters[key] = value.permalink_pt // Extract just the string - } else {
-
cleanedFilters[key] = value
-
if (value != null) {
-
if (key === 'mostrasFilter' && typeof value === 'object' && value?.filter_value) { -
cleanedFilters[key] = value.filter_value // Extract just the string -
} else if (key === 'sessao' && typeof value === 'object' && value?.filter_value) { -
cleanedFilters[key] = value.filter_value // Extract just the string -
} else if (key === 'paisesFilter' && typeof value === 'object' && value?.filter_value) { -
cleanedFilters[key] = value.filter_value // Extract just the string -
} else if (key === 'cinemasFilter' && typeof value === 'object' && value?.filter_value) { -
cleanedFilters[key] = value.filter_value // Extract just the string -
} else { -
cleanedFilters[key] = value -
}} } router.get(props.tabBaseUrl, cleanedFilters, { @@ -100,6 +112,30 @@ const { sentinel, isSticky } = useStickyMenuTabs() <MobileTrigger @open-menu="openMenu" />
<!-- { "query": null,
"mostrasFilter": null,
"cinemasFilter": null,
"paisesFilter": null,
"sessao": { "sessao": "2000-01-01T19:00:00.000Z",
"display_sessao": "19:00",
"filter_value": "19h00",
"filter_display": "19h00" }
} -->
class="flex gap-300 pt-200 pb-300"
v-if="Object.values(props.current_filters).some((item) => item !== null)"
<TagFilter
v-for="[key, value] in Object.entries(props.current_filters).filter(([k, v]) => v !== null)"
:key="key"
:filter="{ label: value.filter_display, value: value.filter_value }"
:text="value.filter_display"
@remove-filter="removeQuery"
/>
@@ -132,6 +168,9 @@ const { sentinel, isSticky } = useStickyMenuTabs() :model-value="modelValue" :update-field="updateField" :mostrasFilter="props.mostrasFilter"
-
:cinemasFilter="props.cinemasFilter" -
:paisesFilter="props.paisesFilter" -
:sessoes="props.sessoes" /> </template> </ResponsiveFilterMenu>
diff --git a/app/models/cinema.rb b/app/models/cinema.rb index 2c0c420..9ca1bd6 100644 --- a/app/models/cinema.rb +++ b/app/models/cinema.rb @@ -2,4 +2,12 @@ class Cinema < ApplicationRecord belongs_to :bairro belongs_to :edicao has_many :programacoes +
- def filter_value
- id
- end
- def filter_display
- nome
- end end diff --git a/app/models/mostra.rb b/app/models/mostra.rb index 7dee645..034b02a 100644 --- a/app/models/mostra.rb +++ b/app/models/mostra.rb @@ -8,7 +8,7 @@ class Mostra < ApplicationRecord "cine-memoria": "tag-cine-memoria", "midnight-movies": "tag-midnight-movies", "cinema-capacete": "tag-cinema-capacete",
- "classicos-cults": "tag-classicos--cults",
-
"classicos-cults": "tag-classicos-cults", "expectativa": "tag-expectativas", "itinerarios-unicos": "tag-itinerarios-unicos", "panorama-mundial": "tag-panorama-mundial" @@ -25,4 +25,12 @@ class Mostra < ApplicationRecord def display_name nome_abreviado.split(/[-:]/).first end
-
def filter_value
-
permalink_pt
-
end
-
def filter_display
-
display_name
-
end end diff --git a/app/models/pais.rb b/app/models/pais.rb index f3cb26f..c382cd2 100644 --- a/app/models/pais.rb +++ b/app/models/pais.rb @@ -1,3 +1,11 @@ class Pais < ApplicationRecord has_many :paises_peliculas
-
def filter_value
-
id
-
end
-
def filter_display
-
nome_pais
-
end end diff --git a/app/models/pelicula.rb b/app/models/pelicula.rb index abc6681..48ec6c1 100644 --- a/app/models/pelicula.rb +++ b/app/models/pelicula.rb @@ -1,4 +1,6 @@ class Pelicula < ApplicationRecord
-
include ActionView::Helpers::TextHelper
-
belongs_to :importacao belongs_to :edicao belongs_to :mostra @@ -18,4 +20,15 @@ class Pelicula < ApplicationRecord } catalogo_ficha_2007.split(" ")&.first.split("/")[locales[I18n.locale]] || "TBD" end
-
def display_paises
-
all_paises = paises.map { |it| it.nome_pais }
-
counter = all_paises.length
-
if counter > 1
-
"#{all_paises.first} + #{pluralize(counter - 1, "país", "países")}" -
else
-
all_paises.first -
end
-
end end diff --git a/app/models/programacao.rb b/app/models/programacao.rb index 9a83441..639fa4b 100644 --- a/app/models/programacao.rb +++ b/app/models/programacao.rb @@ -10,4 +10,12 @@ class Programacao < ApplicationRecord def display_sessao sessao.strftime("%H:%M") end
-
def filter_value
-
sessao.strftime("%Hh%M")
-
end
-
def filter_display
-
sessao.strftime("%Hh%M")
-
end end diff --git a/config/routes.rb b/config/routes.rb index c88daeb..49357fd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,7 @@ Rails.application.routes.draw do
resources :edicoes_anteriores, only: [ :index ]
-
resources :mostras, only: [ :index ] end
diff --git a/test/controllers/programs_controller_test.rb b/test/controllers/programs_controller_test.rb index 8c23ecf..a94cd03 100644 --- a/test/controllers/programs_controller_test.rb +++ b/test/controllers/programs_controller_test.rb @@ -462,7 +462,7 @@ class ProgramsControllerTest < ActionDispatch::IntegrationTest props = inertia_props
mostras_filter = props["mostrasFilter"]
- assert_equal 3, mostras_filter.length
-
assert_equal 4, mostras_filter.length
permalinks = mostras_filter.map { |m| m["permalink_pt"] } @@ -476,4 +476,183 @@ class ProgramsControllerTest < ActionDispatch::IntegrationTest assert mostra["permalink_pt"].present? assert mostra["nome_abreviado"].present? end
-
test "provides correct cinemas filters options" do
-
get program_url
-
assert_response :success
-
props = inertia_props
-
cinema_options = props["cinemasFilter"]
-
assert_equal 2, cinema_options.length
-
end
-
test "filters by cinema - cine brasilia" do
-
cine_brasilia = cinemas(:cine_brasilia)
-
get program_url, params: { cinemasFilter: cine_brasilia.id }
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
assert_equal 5, elements.length
-
total_elements = props["pagy"]["count"]
-
assert_equal 17, total_elements
-
end
-
test "filters by cinema - cinepolis" do
-
cinepolis = cinemas(:cinepolis)
-
get program_url, params: { cinemasFilter: cinepolis.id }
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
titles = elements.map { |e| e["titulo"] }
-
expected_titles = [ "Cidade Perdida", "Cidade em Transformação", "Amor em Brasília", "Berlin Nights", "São Paulo" ]
-
expected_titles.each { |title| assert_includes titles, title }
-
end
-
test "handles invalid cinema filter gracefully" do
-
get program_url, params: { cinemasFilter: 999_999 }
-
assert_response :success
-
props = inertia_props
-
assert props["elements"].length > 0
-
selected_filters = props["current_filters"]
-
assert_nil selected_filters["cinemasFilter"]
-
end
-
test "combines search query and cinema filter" do
-
cinepolis = cinemas(:cinepolis)
-
get program_url, params: {
-
query: "Batman", -
cinemasFilter: cinepolis.id -
}
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
assert_equal 1, elements.length
-
assert_equal "Batman", elements.first["titulo"]
-
assert_equal "Batman", props["current_filters"]["query"]
-
assert_equal cinepolis.id, props["current_filters"]["cinemasFilter"]["id"]
-
end
-
test "combines search query and cinema filter with no results" do
-
cine_brasilia = cinemas(:cine_brasilia)
-
get program_url, params: {
-
query: "Batman", -
cinemasFilter: cine_brasilia.id -
}
-
assert_response :success
-
props = inertia_props
-
assert_equal 0, props["elements"].length
-
assert_equal "Batman", props["current_filters"]["query"]
-
assert_equal cine_brasilia.id, props["current_filters"]["cinemasFilter"]["id"]
-
end
-
test "search finds movies across different cinemas" do
-
get program_url, params: { query: "Cidade" }
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
titles = elements.map { |e| e["titulo"] }
-
assert_includes titles, "Cidade Perdida"
-
assert_includes titles, "Cidade em Transformação"
-
end
-
test "cinema filter affects available dates" do
-
cine_brasilia = cinemas(:cine_brasilia)
-
get program_url, params: { cinemasFilter: cine_brasilia.id }
-
assert_response :success
-
props = inertia_props
-
available_dates = props["menuTabs"].map { _1["date"] }
-
assert_equal [ "2024-10-07" ], available_dates.uniq
-
end
-
test "preserves cinema filter when navigating dates" do
-
cinepolis = cinemas(:cinepolis)
-
get program_url, params: {
-
cinemasFilter: cinepolis.id, -
date: "2024-10-06" -
}
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
titles = elements.map { |e| e["titulo"] }
-
assert_includes titles, "Amazônia Selvagem"
-
assert_includes titles, "Matrix"
-
assert_equal cinepolis.id, props["current_filters"]["cinemasFilter"]["id"]
-
end
-
test "combines all filters - search, cinema, and date" do
-
cinepolis = cinemas(:cinepolis)
-
get program_url, params: {
-
query: "Cidade", -
cinemasFilter: cinepolis.id, -
date: "2024-10-07" -
}
-
assert_response :success
-
props = inertia_props
-
elements = props["elements"]
-
assert_equal 1, elements.length
-
assert_equal "Cidade em Transformação", elements.first["titulo"]
-
assert_equal "Cidade", props["current_filters"]["query"]
-
assert_equal cinepolis.id, props["current_filters"]["cinemasFilter"]["id"]
-
assert_includes props["menuTabs"].map { _1["date"] }, "2024-10-07"
-
end
-
test "cinema filter with pagination" do
-
cine_brasilia = cinemas(:cine_brasilia)
-
get program_url, params: { cinemasFilter: cine_brasilia.id }
-
assert_response :success
-
props = inertia_props
-
assert_equal 5, props["elements"].length
-
assert_equal 1, props["pagy"]["page"]
-
assert_equal 17, props["pagy"]["count"]
-
end
-
test "returns correct cinemasFilter options in props" do
-
get program_url
-
assert_response :success
-
props = inertia_props
-
cinemas_filter = props["cinemasFilter"]
-
assert_equal 2, cinemas_filter.length
-
names = cinemas_filter.map { _1["nome"] }
-
assert_includes names, "Cinépolis Lagoon"
-
assert_includes names, "Cine Brasília"
-
cinema = cinemas_filter.first
-
assert cinema["id"].present?
-
assert cinema["nome"].present?
-
end end diff --git a/test/fixtures/cinemas.yml b/test/fixtures/cinemas.yml index b28df45..9045272 100644 --- a/test/fixtures/cinemas.yml +++ b/test/fixtures/cinemas.yml @@ -2,7 +2,7 @@ cinepolis: nome: "Cinépolis Lagoon" bairro_id: 1
- edicao_id: 1
- edicao_id: 12 endereco: "Av. Borges de Medeiros, 1424 - Lagoa" telefone: "(21) 99999-9999" capacidade: "300" diff --git a/test/fixtures/mostras.yml b/test/fixtures/mostras.yml index f6c6b41..3aa744e 100644 --- a/test/fixtures/mostras.yml +++ b/test/fixtures/mostras.yml @@ -3,7 +3,7 @@ sci_fi: permalink_pt: "sci-fi" permalink_en: "sci-fi" importacao_id: 1
- edicao_id: 1
- edicao_id: 12 nome_pt: "Ficção Científica" nome_en: "Science Fiction" chamada_pt: "Mostra de filmes de ficção científica" diff --git a/test/fixtures/paises.yml b/test/fixtures/paises.yml new file mode 100644 index 0000000..fb85c0b --- /dev/null +++ b/test/fixtures/paises.yml @@ -0,0 +1,10 @@ +usa:
- nome_pais: Estados Unidos +br:
- nome_pais: Brasil +ar:
- nome_pais: Argentina +fr:
- nome_pais: França +ger:
- nome_pais: Alemanha diff --git a/test/fixtures/paises_peliculas.yml b/test/fixtures/paises_peliculas.yml new file mode 100644 index 0000000..1c37ed0 --- /dev/null +++ b/test/fixtures/paises_peliculas.yml @@ -0,0 +1,46 @@ +batman_usa:
- pais: usa
- pelicula: batman
+special_char_br:
- pais: br
- pelicula: special_char
+matrix_usa:
- pais: usa
- pelicula: matrix
+cidade_perdida_ar:
- pais: ar
- pelicula: cidade_perdida
+cidade_perdida_br:
- pais: br
- pelicula: cidade_perdida
+amor_brasilia_usa:
- pais: usa
- pelicula: amor_brasilia
+berlin_nights_ger:
- pais: ger
- pelicula: berlin_nights
+paris_stories_fr:
- pais: fr
- pelicula: paris_stories
+amazonia_selvagem_br:
- pais: br
- pelicula: amazonia_selvagem
+cidade_transformacao_br:
- pais: br
- pelicula: cidade_transformacao
+<% 17.times do |i| %> +paises_peliculas_<%= i %>:
- pais: br
- pelicula: test_<%= i %> +<% end %> diff --git a/test/fixtures/peliculas.yml b/test/fixtures/peliculas.yml index ed0b58e..aa89741 100644 --- a/test/fixtures/peliculas.yml +++ b/test/fixtures/peliculas.yml @@ -6,7 +6,7 @@ batman: titulo_portugues_semartigo: "Batman" permalink: "batman-link" importacao_id: 1
- edicao_id: 1
- edicao_id: 12 mostra: sci_fi imagem: "batman.jpg" duracao_coord_int: 140 @@ -26,7 +26,7 @@ special_char: titulo_portugues_semartigo: "São Paulo" permalink: "sao-paulo-link" importacao_id: 1
- edicao_id: 1
- edicao_id: 12 mostra: sci_fi imagem: "sao-paulo.jpg" duracao_coord_int: 140 @@ -46,7 +46,7 @@ matrix: titulo_portugues_semartigo: "Matrix" permalink: "matrix-link" importacao_id: 1
- edicao_id: 1
- edicao_id: 12 mostra: sci_fi imagem: "matrix.jpg" duracao_coord_int: 136 @@ -67,7 +67,7 @@ test_<%= i %>: titulo_portugues_semartigo: "test_<%= i %>" permalink: "test-<%= i %>-link" importacao_id: 1
- edicao_id: 1
- edicao_id: 12 mostra: sci_fi imagem: "matrix.jpg" duracao_coord_int: 136 diff --git a/test/fixtures/programacoes.yml b/test/fixtures/programacoes.yml index 301648b..550e637 100644 --- a/test/fixtures/programacoes.yml +++ b/test/fixtures/programacoes.yml @@ -81,8 +81,8 @@ program_matrix:
<% 17.times do |i| %> -program_batman_<%= i %>:
- cinema: cinepolis +program_test_<%= i %>:
- cinema: cine_brasilia pelicula: test_<%= i %> importacoesprog_id: 123 edicao_id: 12 @@ -145,7 +145,7 @@ programa_amor_brasilia_1: updated: <%= Date.yesterday %>
programa_berlin_nights_1:
- cinema: cine_brasilia
- cinema: cinepolis pelicula: berlin_nights importacoesprog_id: 123 edicao_id: 12 @@ -165,7 +165,7 @@ programa_berlin_nights_1: updated: <%= Date.yesterday %>
programa_paris_stories_1:
- cinema: cine_brasilia
- cinema: cinepolis pelicula: paris_stories importacoesprog_id: 123 edicao_id: 12 @@ -205,7 +205,7 @@ programa_amazonia_selvagem_1: updated: <%= Date.yesterday %>
programa_cidade_transformacao_0:
- cinema: cine_brasilia
- cinema: cinepolis pelicula: cidade_transformacao importacoesprog_id: 123 edicao_id: 12 @@ -225,7 +225,7 @@ programa_cidade_transformacao_0: updated: <%= Date.yesterday %>
programa_cidade_transformacao_1:
- cinema: cine_brasilia
- cinema: cinepolis pelicula: cidade_transformacao importacoesprog_id: 123 edicao_id: 12 @@ -285,7 +285,7 @@ programa_berlin_nights_2: updated: <%= Date.yesterday %>
programa_cidade_transformacao_2:
- cinema: cine_brasilia
- cinema: cinepolis pelicula: cidade_transformacao importacoesprog_id: 123 edicao_id: 12