diff --git a/src/features/statistics/StatisticsUtils.ts b/src/features/statistics/StatisticsUtils.ts
index a961edc..855784a 100644
--- a/src/features/statistics/StatisticsUtils.ts
+++ b/src/features/statistics/StatisticsUtils.ts
@@ -34,7 +34,7 @@ export const normalizeArray = (array: number[]): number[] => {
};
export interface MovieStatistics {
- movieTitle: string;
+ title: string;
dateWatched: string;
vote_average: number;
revenue: number;
@@ -156,7 +156,7 @@ export const loadDefaultChartSettings = (params: {
showInLegend: false,
tooltip: {
renderer: (params: AgScatterSeriesTooltipRendererParams) =>
- `
${params.datum.movieTitle}
` +
+ `${params.datum.title}
` +
`${params.xName}: ${params.xValue} ${params.yName}: ${params.yValue}
`,
},
},
diff --git a/src/features/statistics/StatisticsView.vue b/src/features/statistics/StatisticsView.vue
index dbb2f29..bd4f9ad 100644
--- a/src/features/statistics/StatisticsView.vue
+++ b/src/features/statistics/StatisticsView.vue
@@ -7,16 +7,37 @@
/>
- {{ normButtonText }}
-
-
- Normalizing scores adjusts each member's ratings to account for their different scoring patterns.
- A normalized score of 0 means average, while lower and higher values indicate scores below and above your usual rating.
-
+
+
+
+
+
+
+
+
{{ normButtonText }}
+
+ Normalizing scores adjusts each member's ratings to account for their different scoring patterns.
+ A normalized score of 0 means average, while lower and higher values indicate scores below and above your usual rating.
+
+
+
+
@@ -48,24 +69,7 @@
-->
-
-
-
-
-
-
-
-
-
-
+
@@ -74,7 +78,18 @@
import { AgHistogramSeriesTooltipRendererParams, AgBarSeriesTooltipRendererParams } from "ag-charts-community";
import { AgChartsVue } from "ag-charts-vue3";
import { DateTime } from "luxon";
-import { ref, computed, watch } from "vue";
+import { ref, computed, watch, h } from "vue";
+import { filterMovies } from '@/common/searchMovies';
+import VAvatar from "@/common/components/VAvatar.vue";
+import AverageImg from "@/assets/images/average.svg";
+import MovieTooltip from "@/features/reviews/components/MovieTooltip.vue";
+import {
+ createColumnHelper,
+ getCoreRowModel,
+ getSortedRowModel,
+ useVueTable,
+} from "@tanstack/vue-table";
+import TableView from "@/features/reviews/components/TableView.vue";
import {
normalizeArray,
@@ -135,12 +150,20 @@ const loading = computed(
loadingCalculations.value,
);
+const searchTerm = ref("");
+const searchInput = ref(null);
+const showTooltip = ref(false);
+
+const filteredMovieData = computed(() => {
+ return filterMovies(movieData.value, searchTerm.value);
+});
+
const fetchMovieData = (reviews: DetailedReviewListItem[]) => {
return reviews.map((review) => {
if (!review.externalData) return null;
return {
- movieTitle: review.title,
+ title: review.title,
dateWatched: DateTime.fromISO(review.createdDate).toLocaleString(),
...Object.keys(review.scores).reduce>(
(acc, key) => {
@@ -150,6 +173,8 @@ const fetchMovieData = (reviews: DetailedReviewListItem[]) => {
{},
),
// Map the new external data structure
+ imageUrl: review.imageUrl,
+ createdDate: review.createdDate,
vote_average: review.externalData.vote_average,
revenue: review.externalData.revenue,
budget: review.externalData.budget,
@@ -157,6 +182,7 @@ const fetchMovieData = (reviews: DetailedReviewListItem[]) => {
genres: review.externalData.genres,
production_companies: review.externalData.production_companies,
production_countries: review.externalData.production_countries,
+ externalData: review.externalData,
};
}).filter(Boolean); // Remove any null entries
};
@@ -257,11 +283,40 @@ watch(selectedChartBase, generateCustomChart);
const loadChartOptions = async () => {
try {
chartLoadingStates.value.histogram = true;
+ // Filter the histogram data based on the filtered movies
+ const filteredHistData = histogramData.value.map(bin => {
+ const filtered = { ...bin };
+ members.value.forEach(member => {
+ filtered[member.id] = 0;
+ });
+ return filtered;
+ });
+ const filteredHistNormData = histogramNormData.value.map(bin => {
+ const filtered = { ...bin };
+ members.value.forEach(member => {
+ filtered[member.id] = 0;
+ });
+ return filtered;
+ });
+
+ // Populate the filtered histogram data
+ filteredMovieData.value.forEach(movie => {
+ members.value.forEach(member => {
+ const score = Math.floor(movie[member.id]);
+ if (!isNaN(score)) {
+ filteredHistData[score][member.id] += 1;
+ }
+ let scoreNorm = Math.floor(movie[member.id + "Norm"] * 4 + 5);
+ scoreNorm = scoreNorm < 0 ? 0 : scoreNorm > 10 ? 10 : scoreNorm;
+ filteredHistNormData[scoreNorm][member.id] += 1;
+ });
+ });
+
histChartOptions.value = {
autoSize: true,
theme: "ag-default-dark",
title: { text: "Score Histogram" },
- data: normalize.value ? histogramNormData.value : histogramData.value,
+ data: normalize.value ? filteredHistNormData : filteredHistData,
series: members.value.map((member) => {
return {
type: "line",
@@ -315,7 +370,7 @@ const loadChartOptions = async () => {
chartLoadingStates.value.histogram = false;
// Special handling for TMDB score chart where TMDB score is available
- const validTMDBData = movieData.value.filter(movie =>
+ const validTMDBData = filteredMovieData.value.filter(movie =>
movie.vote_average && movie.vote_average > 0 &&
movie.average && movie.average > 0
);
@@ -341,7 +396,7 @@ const loadChartOptions = async () => {
yData: "average",
normalizeY: true,
normalizeToggled: normalize.value,
- movieData: movieData.value,
+ movieData: filteredMovieData.value,
});
revenueChartOptions.value = loadDefaultChartSettings({
@@ -353,7 +408,7 @@ const loadChartOptions = async () => {
yData: "average",
normalizeY: true,
normalizeToggled: normalize.value,
- movieData: movieData.value,
+ movieData: filteredMovieData.value,
});
dateChartOptions.value = loadDefaultChartSettings({
@@ -365,7 +420,7 @@ const loadChartOptions = async () => {
yData: "average",
normalizeY: true,
normalizeToggled: normalize.value,
- movieData: movieData.value,
+ movieData: filteredMovieData.value,
});
genreChartOptions.value = generateGenreChart();
@@ -454,7 +509,7 @@ const normName = (name = "average") => {
const headers = computed(() => {
const headers: Header[] = [
- { value: "movieTitle", style: "font-bold", title: "Title" },
+ { value: "title", style: "font-bold", title: "Title" },
{ value: "dateWatched", title: "Date Reviewed" },
];
@@ -470,4 +525,74 @@ const headers = computed(() => {
});
return headers;
});
+
+// Add a watch on filteredMovieData to trigger chart updates
+watch(filteredMovieData, () => {
+ if (filteredMovieData.value) {
+ loadChartOptions();
+ }
+}, { immediate: true });
+
+const columnHelper = createColumnHelper();
+
+const columns = computed(() => [
+ columnHelper.accessor("title", {
+ header: "Title",
+ cell: (info) => h(MovieTooltip, {
+ title: info.getValue(),
+ imageUrl: info.row.original.imageUrl,
+ movie: info.row.original.externalData
+ }),
+ meta: {
+ class: "font-bold",
+ },
+ }),
+ columnHelper.accessor("dateWatched", {
+ header: "Date Reviewed",
+ }),
+ ...members.value.map((member) =>
+ columnHelper.accessor(normName(member.id), {
+ id: normName(member.id),
+ header: () => h(VAvatar, {
+ src: member.image,
+ name: member.name,
+ }),
+ cell: (info) => {
+ const value = info.getValue();
+ return value !== undefined ? Math.round(value * 100) / 100 : '';
+ },
+ sortUndefined: "last",
+ }),
+ ),
+ columnHelper.accessor(normName(), {
+ header: () => h("img", {
+ src: AverageImg,
+ class: "h-12 w-16 max-w-none"
+ }),
+ cell: (info) => {
+ const value = info.getValue();
+ return value !== undefined ? Math.round(value * 100) / 100 : '';
+ },
+ sortUndefined: "last",
+ }),
+ columnHelper.accessor("vote_average", {
+ header: "TMDB",
+ cell: (info) => {
+ const value = info.getValue();
+ return value !== undefined ? Math.round(value * 100) / 100 : '';
+ },
+ }),
+]);
+
+const movieTable = useVueTable({
+ get columns() {
+ return columns.value;
+ },
+ get data() {
+ return filteredMovieData.value ?? [];
+ },
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getRowId: (row) => row.title,
+});