index.vue 5.59 KB
<script lang="ts" setup>
//#region --import--.
import { ArrowLeftIcon } from "lucide-vue-next";
import { useTMDB } from "~/composables/tMDB";
import { computed, onMounted, ref } from "vue";
import { Movie } from "~/models/movie";
import type { MovieInterface } from "~/interfaces/movie";
import { Credit } from "~/models/credit";
import type { CreditsResponse } from "~/interfaces/credit";
import type { Comment } from "~/type/commentForm";
//#endregion

//#region --Declaration--.
const { fetchMovieDetails, fetchMovieCredits } = useTMDB();
//#endregion

//#region --Declaration--.
const { currentRoute } = useRouter();
//#endregion

//#region --Data/ref--.
const isLoading = ref(true);
const isSubmitting = ref(false);
//#endregion

//#region --Computed--.
const movieId = computed(() => {
  if (currentRoute.value.params.id) {
    if (typeof currentRoute.value.params.id === "string") {
      if (typeof Number(+currentRoute.value.params.id) === "number") {
        return +currentRoute.value.params.id as number;
      } else {
        return currentRoute.value.params.id as string;
      }
    } else {
      return null;
    }
  } else {
    return null;
  }
});

const movie = computed(() => {
  if (unref(movieId)) {
    // Todo : revoir ici.
    return useRepo(Movie).query().where("id", movieId.value).withAll().first() as unknown as MovieInterface;
  } else {
    return null;
  }
});

/**
 * Computed property for director
 */
const director = computed(() => {
  if (unref(movie)?.credit?.crew) {
    return movie.value?.credit.crew.find((person) => person.job === "Director");
  } else {
    return null;
  }
});
//#endregion

//#region --Function--.
/**
 * Fetch movie details
 */
const fetchDetails = async (id: number | string) => {
  try {
    isLoading.value = true;

    const data = await fetchMovieDetails(id);
    // Add to store collection.
    useRepo(Movie).save(data);
  } catch (error) {
    console.error("Error fetching movie details:", error);
  } finally {
    isLoading.value = false;
  }
};

/**
 * Format runtime
 * @param minutes
 */
const formatRuntime = (minutes: number) => {
  if (!minutes) return "Durée inconnue";
  // Find nb hours.
  const hours = Math.floor(minutes / 60);
  // Find last minutes.
  const mins = minutes % 60;

  return `${hours}h ${mins}min`;
};

async function fetchCredits(id: number | string) {
  try {
    const data = (await fetchMovieCredits(id)) as CreditsResponse;
    data.movie_id = id;
    // Add to store collection.
    useRepo(Credit).save(data);
  } catch (error) {
    console.error("Error fetching movie credits:", error);
  }
}

function handleSubmitEvent(event: Comment) {
  //todo : faire la gestion des commentaire avec un nouveau model + liaison.
  console.log('submitted', event)
}

//#endregion

//#region --Global event--.
onMounted(() => {
  // Fetch data on component mount.
  if (unref(movieId)) {
    const id = unref(movieId) as string | number;
    fetchDetails(id);
    fetchCredits(id);
  }
  // loadComments()
});
//#endregion
</script>

<template>
  <section>
    <!-- Skeleton loader pendant le chargement -->
    <ui-components-skeleton-movie-detail-loader v-if="isLoading" />

    <!-- Contenu du film -->
    <div v-else-if="movie" class="relative">
      <!-- Backdrop image -->
      <ui-components-backdrop-image v-if="movie.backdrop_path" :src="movie.backdrop_path" :title="movie.title" />

      <!-- Contenu principal -->
      <div class="container mx-auto px-4 py-8 relative z-10 pt-20">
        <button
          class="flex items-center text-gray-400 hover:text-white mb-8 transition-colors"
          @click="navigateTo('/')"
        >
          <ArrowLeftIcon :size="20" class="mr-2" />
          Retour
        </button>

        <div class="flex flex-col md:flex-row gap-8">
          <!-- Poster -->
          <ui-components-poster v-if="movie.poster_path" :src="movie.poster_path" :title="movie.title" />

          <!-- Informations du film -->
          <section class="w-full md:w-2/3 lg:w-3/4">
            <h1 class="text-3xl md:text-4xl font-bold mb-2">{{ movie.title }}</h1>
            <p v-if="movie.release_date" class="text-gray-400 mb-4">
              {{ useDateFormat(movie.release_date, "DD-MM-YYYY") }} • {{ formatRuntime(movie.runtime) }}
            </p>

            <!-- Note et votes -->
            <details-score-and-vote :nb-vote="movie.vote_count" :score="movie.vote_average" />

            <!-- Genres -->
            <details-movie-gender :genres="movie.genres" />

            <!-- Synopsis -->
            <div class="mb-6">
              <h2 class="text-xl font-bold mb-2">Synopsis</h2>
              <p class="text-gray-300">{{ movie.overview || "Aucun synopsis disponible." }}</p>
            </div>

            <!-- Réalisateur et têtes d'affiche -->
            <div v-if="movie.credit" class="mb-6">
              <h2 class="text-xl font-bold mb-2">Équipe</h2>
              <div v-if="director" class="mb-2">
                <span class="font-semibold">Réalisateur:</span> {{ director.name }}
              </div>
              <div v-if="movie.credit.cast.length > 0">
                <span class="font-semibold">Têtes d'affiche:</span>
                {{
                  movie.credit.cast
                    .slice(0, 10)
                    .map((person) => person.name)
                    .join(", ")
                }}
              </div>
            </div>
          <!--  Comments form.  -->
            <h3 class="text-xl font-bold mt-8 mb-4">Ajouter un commentaire</h3>
            <form-movie-comment-form @event:submit="handleSubmitEvent" />
          </section>
        </div>
      </div>
    </div>
  </section>
</template>

<style scoped></style>