diff --git a/src/backend/plugins/video/warren_video/api.py b/src/backend/plugins/video/warren_video/api.py
index 625d65c7e..41b049fb0 100644
--- a/src/backend/plugins/video/warren_video/api.py
+++ b/src/backend/plugins/video/warren_video/api.py
@@ -44,6 +44,7 @@ async def views(
video_id: IRI, filters: Annotated[BaseQueryFilters, Depends()]
) -> VideoViews:
"""Video views."""
+
query = {
"verb": PlayedVerb().id,
"activity": video_id,
diff --git a/src/frontend/apps/web/pages/index.tsx b/src/frontend/apps/web/pages/index.tsx
index 7c5458334..2ca43f781 100644
--- a/src/frontend/apps/web/pages/index.tsx
+++ b/src/frontend/apps/web/pages/index.tsx
@@ -1,20 +1,14 @@
import type { ReactElement } from "react";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import Layout from "../components/Layout";
import type { NextPageWithLayout } from "./_app";
import { DailyViews } from "ui";
+import Wip from "ui/components/wip";
+import AppProvider from "ui/provider/app";
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- refetchOnWindowFocus: false,
- },
- },
-});
const Web: NextPageWithLayout = () => {
+
const videoIds = [
"uuid://0aecfa93-cef3-45ae-b7f5-a603e9e45f50",
"uuid://1c0c127a-f121-4bd1-8db6-918605c2645d",
@@ -28,10 +22,10 @@ const Web: NextPageWithLayout = () => {
"uuid://e151ee65-7a72-478c-ac57-8a02f19e748b",
];
return (
-
+
+
-
-
+
);
};
diff --git a/src/frontend/packages/ui/components/wip.tsx b/src/frontend/packages/ui/components/wip.tsx
new file mode 100644
index 000000000..4dbe6a8d0
--- /dev/null
+++ b/src/frontend/packages/ui/components/wip.tsx
@@ -0,0 +1,24 @@
+import React, {useRef} from 'react'
+import useDate from "../hooks/useDate";
+
+
+// todo : to be replaced by a proper range component.
+const Wip = () => {
+
+ const {setDate} = useDate()
+
+ const sinceInput = useRef(null);
+ const untilInput = useRef(null);
+
+ return (
+ <>
+
+
+
+ >
+ )
+}
+export default Wip;
\ No newline at end of file
diff --git a/src/frontend/packages/ui/contexts/dateContext.tsx b/src/frontend/packages/ui/contexts/dateContext.tsx
new file mode 100644
index 000000000..5ca43904b
--- /dev/null
+++ b/src/frontend/packages/ui/contexts/dateContext.tsx
@@ -0,0 +1,20 @@
+import React, {createContext, Dispatch, SetStateAction, useState} from 'react'
+
+
+export interface DateContextType {
+ date: [string, string];
+ setDate: Dispatch>,
+}
+
+const DateContext = createContext(null)
+
+export const DateProvider: React.FC<{ children: any }> = ({ children }) => {
+ const [date, setDate] = useState<[string, string]>(['', ''])
+ return (
+
+ {children}
+
+ )
+}
+
+export default DateContext
\ No newline at end of file
diff --git a/src/frontend/packages/ui/hooks/useDate.tsx b/src/frontend/packages/ui/hooks/useDate.tsx
new file mode 100644
index 000000000..8740463f1
--- /dev/null
+++ b/src/frontend/packages/ui/hooks/useDate.tsx
@@ -0,0 +1,14 @@
+import { useContext } from 'react'
+import DateContext, {DateContextType} from "../contexts/dateContext";
+
+
+// todo : refactor in the contexts/dateContext.tsx file ?
+const useDate = (): DateContextType => {
+ const value = useContext(DateContext)
+ if (!value) {
+ throw new Error(`Missing wrapping Provider for Store DateContextType`);
+ }
+ return value;
+}
+
+export default useDate
\ No newline at end of file
diff --git a/src/frontend/packages/ui/libs/axios.ts b/src/frontend/packages/ui/libs/axios.ts
new file mode 100644
index 000000000..73da37020
--- /dev/null
+++ b/src/frontend/packages/ui/libs/axios.ts
@@ -0,0 +1,6 @@
+import Axios from "axios";
+
+
+export const axios = Axios.create({
+ baseURL: `${process.env.NEXT_PUBLIC_WARREN_BACKEND_ROOT_URL}/api/v1/`,
+});
\ No newline at end of file
diff --git a/src/frontend/packages/ui/libs/react-query.ts b/src/frontend/packages/ui/libs/react-query.ts
new file mode 100644
index 000000000..d47fa0ba4
--- /dev/null
+++ b/src/frontend/packages/ui/libs/react-query.ts
@@ -0,0 +1,10 @@
+import {DefaultOptions, QueryClient} from "@tanstack/react-query";
+
+
+const queryConfig: DefaultOptions = {
+ queries: {
+ refetchOnWindowFocus: false,
+ },
+};
+
+export const queryClient = new QueryClient({defaultOptions:queryConfig})
\ No newline at end of file
diff --git a/src/frontend/packages/ui/provider/app.tsx b/src/frontend/packages/ui/provider/app.tsx
new file mode 100644
index 000000000..91013a629
--- /dev/null
+++ b/src/frontend/packages/ui/provider/app.tsx
@@ -0,0 +1,19 @@
+import {DateProvider} from "../contexts/dateContext";
+import {ReactQueryDevtools} from "@tanstack/react-query-devtools";
+import {QueryClientProvider} from "@tanstack/react-query";
+import {queryClient} from "../libs/react-query";
+
+
+type AppProviderProps = {
+ children: React.ReactNode;
+};
+
+const AppProvider = ({ children }: AppProviderProps) => (
+
+
+ {children}
+
+
+
+ )
+export default AppProvider;
\ No newline at end of file
diff --git a/src/frontend/packages/ui/video/Views.tsx b/src/frontend/packages/ui/video/Views.tsx
index 0d19ece5c..d8e3de43f 100644
--- a/src/frontend/packages/ui/video/Views.tsx
+++ b/src/frontend/packages/ui/video/Views.tsx
@@ -1,20 +1,49 @@
-import React, { useState, useEffect } from "react";
+import React, { useState } from "react";
import ReactECharts from "echarts-for-react";
import type { EChartsOption } from "echarts-for-react";
-import { useQuery, useQueries, useQueryClient } from "@tanstack/react-query";
-
-import axios from "axios";
import cloneDeep from "lodash.clonedeep";
+import useDate from "../hooks/useDate";
+import {useVideosViews} from "./api/getVideosViews";
+import {DailyViewsResponseItem} from "./types";
-type DailyViewsResponseItem = {
- day: string;
- views: number;
+type Series = {
+ name: string;
+ data: Array,
+ type: string,
+ smooth: number,
+ symbol: string,
+ areaStyle: any,
+ stack: string,
+ emphasis: any,
};
-type VideoViewsResponse = {
- total: number;
- daily_views: Array;
+const baseOption: EChartsOption = {
+ grid: { top: 80, right: 8, bottom: 100, left: 50 },
+ xAxis: {
+ type: "category",
+ data: [],
+ axisTick: {
+ alignWithLabel: true,
+ interval: 0,
+ },
+ axisLabel: {
+ interval: 0,
+ rotate: 45,
+ height: 200,
+ },
+ },
+ yAxis: {
+ type: "value",
+ name: "# views",
+ },
+ series: [],
+ tooltip: {
+ trigger: "axis",
+ },
+ textStyle: {
+ fontFamily: "Roboto, sans-serif",
+ },
};
type DailyViewsProps = {
@@ -22,78 +51,36 @@ type DailyViewsProps = {
};
export const DailyViews = ({ videoIds }: DailyViewsProps) => {
- const baseOption: EChartsOption = {
- grid: { top: 80, right: 8, bottom: 100, left: 50 },
- xAxis: {
- type: "category",
- data: [],
- axisTick: {
- alignWithLabel: true,
- interval: 0,
- },
- axisLabel: {
- interval: 0,
- rotate: 45,
- height: 200,
- },
- },
- yAxis: {
- type: "value",
- name: "# views",
- },
- series: [],
- tooltip: {
- trigger: "axis",
- },
- textStyle: {
- fontFamily: "Roboto, sans-serif",
- },
- };
+ const {date: [since, until]} = useDate();
const [option, setOption] = useState(baseOption);
const newOption = cloneDeep(option);
- function getVideoViews(videoId: string) {
- return axios
- .get(
- `${process.env.NEXT_PUBLIC_WARREN_BACKEND_ROOT_URL}/api/v1/video/${videoId}/views`
- )
- .then((res) => res.data);
- }
-
function addOneSeries(
videoId: string,
daily_views: Array
) {
newOption.xAxis.data = daily_views.map((d) => d.day);
- newOption.series.push({
- name: videoId,
- data: daily_views.map((d) => d.views),
- type: "line",
- smooth: 0.2,
- symbol: "none",
- areaStyle: {},
- stack: "Total",
- emphasis: {
- focus: "series",
- },
- });
+ newOption.series = [
+ ...newOption.series.filter((series: Series) => series.name != videoId),
+ {
+ name: videoId,
+ data: daily_views.map((d) => d.views),
+ type: "line",
+ smooth: 0.2,
+ symbol: "none",
+ areaStyle: {},
+ stack: "Total",
+ emphasis: {
+ focus: "series",
+ },
+ }
+ ];
setOption(newOption);
}
- const results = useQueries({
- queries: videoIds.map((videoId) => {
- return {
- queryKey: [`videoViews-${videoId}`],
- queryFn: () => getVideoViews(videoId),
- onSuccess: (data: VideoViewsResponse) => {
- addOneSeries(videoId, data.daily_views);
- return data;
- },
- };
- }),
- });
+ const results = useVideosViews({videoIds, since, until, onSuccess: addOneSeries});
if (results.some((result) => result.isLoading))
return Loading...;
diff --git a/src/frontend/packages/ui/video/api/getVideosViews.ts b/src/frontend/packages/ui/video/api/getVideosViews.ts
new file mode 100644
index 000000000..7fc0274e8
--- /dev/null
+++ b/src/frontend/packages/ui/video/api/getVideosViews.ts
@@ -0,0 +1,32 @@
+
+import {useQueries} from "@tanstack/react-query";
+import {DailyViewsResponseItem, VideoViewsResponse} from "../types";
+import {axios} from "../../libs/axios";
+
+
+const getVideoViews = (videoId: string, since: string, until: string) => {
+ return axios.get(`video/${videoId}/views`, {params: {...since && {since: since}, ...until && {until: until}}})
+ .then((res) => res.data);
+};
+
+type UseVideosViewsOptions = {
+ videoIds: Array;
+ since: string;
+ until: string;
+ onSuccess: (id: string, data: Array) => void
+};
+
+export const useVideosViews = ({videoIds, since, until, onSuccess}: UseVideosViewsOptions) => {
+ return useQueries({
+ queries: videoIds.map((videoId) => {
+ return {
+ queryKey: ["videoViews", videoId, since, until],
+ queryFn: () => getVideoViews(videoId, since, until),
+ onSuccess: (data: VideoViewsResponse) => {
+ onSuccess(videoId, data.daily_views);
+ return data;
+ },
+ };
+ }),
+ });
+}
\ No newline at end of file
diff --git a/src/frontend/packages/ui/video/types/index.ts b/src/frontend/packages/ui/video/types/index.ts
new file mode 100644
index 000000000..7eb740b2e
--- /dev/null
+++ b/src/frontend/packages/ui/video/types/index.ts
@@ -0,0 +1,11 @@
+
+
+export type DailyViewsResponseItem = {
+ day: string;
+ views: number;
+};
+
+export type VideoViewsResponse = {
+ total: number;
+ daily_views: Array;
+};
\ No newline at end of file