評価値コンポーネントの作成
画像ファイルの準備
- 下記ファイルをダウンロードする。
- recsyslab / recsys_full / src / frontend / public / img /
star_00.png
star_01.png
star_10.png
star_11.png
- recsyslab / recsys_full / src / frontend / public / img /
frontend$ mv ~/Downloads/star*.png public/img/
データ型
frontend/src/types/data.d.ts
...(略)...
// 映画
export type Movie = {
id: number;
title: string;
year: number;
genres: string[];
imdb_id: number;
tmdb_id: number;
rating: Rating; // <- 追加
};
// ↓追加
// 評価値
export type Rating = {
id: string;
user_id: string;
movie_id: number;
rating: number;
rated_at: string;
};
// ↑追加
コンポーネント
スターコンポーネント
frontend/src/app/components/rating/Star.tsx
import Image from 'next/image';
import React from 'react';
type Props = {
index: number;
width: number;
rating: number;
setRating: Function;
};
const Star = (props: Props) => {
const handleRatingClick = async () => {
const rating = (props.index + 1) / 2;
await props.setRating(rating);
};
return (
<>
<button onClick={() => handleRatingClick()}>
<Image
className="opacity-75 hover:opacity-100 active:scale-125 active:opacity-100"
src={`/img/star_${props.index % 2}${props.index < props.rating * 2 ? 1 : 0}.png`}
alt=""
width={props.width / 2}
height={props.width}
/>
</button>
</>
);
};
export default Star;
評価値コンポーネント
frontend/src/app/components/rating/StarRating.tsx
'use client';
import React, { useState } from 'react';
import Star from './Star';
type Props = {
starWidth: number;
rating: number;
};
const StarRating = (props: Props) => {
const [rating, setRating] = useState<number>(props.rating);
return (
<>
<div className="flex">
{(function () {
const stars = [];
for (let i = 0; i < 10; i++) {
stars.push(
<Star
key={i}
index={i}
width={props.starWidth}
rating={rating}
setRating={setRating}
/>
);
}
return <div>{stars}</div>;
})()}
</div>
</>
);
};
export default StarRating;
映画詳細カードコンポーネント
frontend/src/app/components/movie/MovieCardDetail.tsx
import { Movie, User } from '@/types/data'; // <- Userを追加
import Image from 'next/image';
import React from 'react';
import StarRating from '../rating/StarRating'; // <- 追加
const STAR_WIDTH = 48; // <- 追加
type Props = {
movie: Movie;
user: User; // <- 追加
};
const MovieCardDetail = (props: Props) => {
const img_url = '/img/dummy_poster.png';
return (
<>
<article key={props.movie.id}>
<div className="mx-4 my-4 flex">
...(略)...
</div>
{/* ↓追加 */}
{props.user ? (
<StarRating starWidth={STAR_WIDTH} rating={props.movie.rating?.rating} />
) : (
<></>
)}
{/* ↑追加 */}
</article>
</>
);
};
export default MovieCardDetail;
ページ
映画詳細ページ
frontend/src/app/movies/[id]/page.tsx
import MovieCardDetail from '@/app/components/movie/MovieCardDetail';
import { auth } from '@/auth'; // <- 追加
import getMovie from '@/services/movies/getMovie';
import connectUser from '@/services/users/connectUser';
import getUser from '@/services/users/getUser'; // <- 追加
const Movie = async ({ params }: { params: Promise<{ id: number }> }) => {
await connectUser();
const session = await auth(); // <- 追加
const user = session ? await getUser(session?.user?.email!) : null; // <- 追加
const { id } = await params;
const movieId = id;
const { movie } = await getMovie(movieId);
return (
<>
<MovieCardDetail movie={movie} user={user!} /> {/* <- userを追加 */}
</>
);
};
export default Movie;
ブラウザで下記URLにアクセスしてください。
サインインすると、映画詳細カードに五つのスターで構成される評価値コンポーネントが表示されます。評価値コンポーネント上で任意の評価値をクリックすると、その評価値までのスターが黄色く表示されます。ただし、現時点では、評価値は記憶されないため、ブラウザを更新すると元に戻ります。また、現時点では、ユーザ情報は評価値コンポーネントの表示/非表示の判定のために参照しているだけですので、ユーザ依存の評価値は取得できません。