Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨(frontend) Filter video views #17

Merged
merged 8 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ and this project adheres to
- Remove the elasticsearch backend
- Add the LTI django application
- Rename the API directory to a more descriptive name.
- Add a select and date range picker to the web dashboard.

[unreleased]: https://github.com/openfun/warren
3 changes: 2 additions & 1 deletion src/api/plugins/video/warren_video/indicators.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def __init__(
video_uuid: The UUID of the video on which to compute the metric
date_range: The date range on which to compute the indicator. It has
2 fields, `since` and `until` which are dates or timestamps that must be
in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss.SSSZ")
in ISO format (YYYY-MM-DD, YYYY-MM-DDThh:mm:ss.sss±hh:mm or
YYYY-MM-DDThh:mm:ss.sssZ")
is_unique: If true, multiple views by the same actor are counted as
one
"""
Expand Down
11 changes: 6 additions & 5 deletions src/frontend/apps/docs/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { DailyViews } from "ui";
import AppProvider from "ui/provider/app";
import Filters from "ui/components/filters";

const queryClient = new QueryClient();
export default function Docs() {
return (
<QueryClientProvider client={queryClient}>
<AppProvider>
<h1>Docs</h1>
<DailyViews videoIds={["uuid://8d386f48-3baa-4acf-8a46-0f2be4ae243e"]} />
</QueryClientProvider>
<Filters />
<DailyViews />
</AppProvider>
);
}
2 changes: 1 addition & 1 deletion src/frontend/apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"build-theme": "cunningham -g css -o pages"
},
"dependencies": {
"@openfun/cunningham-react": "^0.1.1",
"@openfun/cunningham-react": "^0.10.0",
"next": "^13.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/apps/web/pages/cunningham-tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@
--c--theme--font--weights--bold: 500;
--c--theme--font--weights--extrabold: 700;
--c--theme--font--weights--black: 900;
--c--theme--font--families--base: Roboto;
--c--theme--font--families--accent: Roboto;
--c--theme--font--families--base: "Roboto Flex Variable", sans-serif;
--c--theme--font--families--accent: "Roboto Flex Variable", sans-serif;
--c--theme--spacings--xl: 4rem;
--c--theme--spacings--l: 3rem;
--c--theme--spacings--b: 1.625rem;
Expand Down
1 change: 1 addition & 0 deletions src/frontend/apps/web/pages/index.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "@openfun/cunningham-react/fonts";
@import "@openfun/cunningham-react/icons";
@import "@openfun/cunningham-react/style";
@import "cunningham-tokens";

Expand Down
32 changes: 6 additions & 26 deletions src/frontend/apps/web/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
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";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
});
import AppProvider from "ui/provider/app";
import Filters from "ui/components/filters";

const Web: NextPageWithLayout = () => {
const videoIds = [
"uuid://0aecfa93-cef3-45ae-b7f5-a603e9e45f50",
"uuid://1c0c127a-f121-4bd1-8db6-918605c2645d",
"uuid://541dab6b-50ae-4444-b230-494f0621f132",
"uuid://69d32ad5-3af5-4160-a995-87e09da6865c",
"uuid://7d4f3c70-1e79-4243-9b7d-166076ce8bfb",
"uuid://8d386f48-3baa-4acf-8a46-0f2be4ae243e",
"uuid://b172ec09-97ec-4651-bc57-6eabebf47ed0",
"uuid://d613b564-5d18-4238-a69c-0fc8cee5d0e7",
"uuid://dd38149d-956a-483d-8975-c1506de1e1a9",
"uuid://e151ee65-7a72-478c-ac57-8a02f19e748b",
];
return (
<QueryClientProvider client={queryClient}>
<DailyViews videoIds={videoIds} />
<ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
<AppProvider>
<Filters />
<DailyViews />
</AppProvider>
);
};

Expand Down
68 changes: 68 additions & 0 deletions src/frontend/packages/ui/components/filters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import useFilters from "../hooks/useFilters";
import { DateRangePicker, Select } from "@openfun/cunningham-react";

type VideoOption = {
value: string;
label: string;
};

const VIDEO_IDS = [
"uuid://0aecfa93-cef3-45ae-b7f5-a603e9e45f50",
"uuid://1c0c127a-f121-4bd1-8db6-918605c2645d",
"uuid://541dab6b-50ae-4444-b230-494f0621f132",
"uuid://69d32ad5-3af5-4160-a995-87e09da6865c",
"uuid://7d4f3c70-1e79-4243-9b7d-166076ce8bfb",
"uuid://8d386f48-3baa-4acf-8a46-0f2be4ae243e",
"uuid://b172ec09-97ec-4651-bc57-6eabebf47ed0",
"uuid://d613b564-5d18-4238-a69c-0fc8cee5d0e7",
"uuid://dd38149d-956a-483d-8975-c1506de1e1a9",
"uuid://e151ee65-7a72-478c-ac57-8a02f19e748b",
];

const Filters: React.FC = () => {
const { date, setDate, setVideoIds } = useFilters();

const getVideoOptions = (): VideoOption[] => {
return VIDEO_IDS.map((item) => ({
value: item,
label: item.slice(-5),
}));
};

const handleVideoIdsChange = (
value: string | number | string[] | undefined
): void => {
const videoIds = Array.isArray(value)
? value
: value
? [value.toString()]
: [];
setVideoIds(videoIds);
};

const handleDateChange = (value: [string, string] | null): void => {
// todo - handle start at 00:00:00 and end at 23:59:59
// todo - component api is going to change soon. Let's wait for its change.
value ? setDate(value) : setDate(["", ""]);
};

return (
<div style={{ display: "flex", gap: "1rem" }}>
<Select
label="Videos"
defaultValue={VIDEO_IDS[0]}
options={getVideoOptions()}
multi={true}
onChange={(e) => handleVideoIdsChange(e.target.value)}
/>
jmaupetit marked this conversation as resolved.
Show resolved Hide resolved
<DateRangePicker
startLabel="Start"
endLabel="End"
value={date}
onChange={(value) => handleDateChange(value)}
/>
</div>
);
};
export default Filters;
27 changes: 27 additions & 0 deletions src/frontend/packages/ui/contexts/filtersContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, {
createContext,
Dispatch,
SetStateAction,
useState,
} from "react";

export interface FiltersContextType {
date: [string, string];
videoIds: Array<string>;
setDate: Dispatch<SetStateAction<[string, string]>>;
setVideoIds: Dispatch<SetStateAction<Array<string>>>;
}

const FiltersContext = createContext<FiltersContextType | null>(null);

export const FiltersProvider: React.FC<{ children: any }> = ({ children }) => {
const [date, setDate] = useState<[string, string]>(["", ""]);
const [videoIds, setVideoIds] = useState<Array<string>>([]);
return (
<FiltersContext.Provider value={{ date, setDate, videoIds, setVideoIds }}>
{children}
</FiltersContext.Provider>
);
};

export default FiltersContext;
12 changes: 12 additions & 0 deletions src/frontend/packages/ui/hooks/useFilters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useContext } from "react";
import FiltersContext, { FiltersContextType } from "../contexts/filtersContext";

const useFilters = (): FiltersContextType => {
const value = useContext(FiltersContext);
if (!value) {
throw new Error(`Missing wrapping Provider for Store FiltersContextType`);
}
return value;
};

export default useFilters;
5 changes: 5 additions & 0 deletions src/frontend/packages/ui/libs/axios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Axios from "axios";

export const axios = Axios.create({
baseURL: `${process.env.NEXT_PUBLIC_WARREN_BACKEND_ROOT_URL}/api/v1/`,
});
9 changes: 9 additions & 0 deletions src/frontend/packages/ui/libs/react-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { DefaultOptions, QueryClient } from "@tanstack/react-query";

const queryConfig: DefaultOptions = {
queries: {
refetchOnWindowFocus: false,
},
};

export const queryClient = new QueryClient({ defaultOptions: queryConfig });
2 changes: 1 addition & 1 deletion src/frontend/packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
"typescript": "^4.5.2"
},
"dependencies": {
"@openfun/cunningham-react": "^0.1.1"
"@openfun/cunningham-react": "^0.10.0"
}
}
22 changes: 22 additions & 0 deletions src/frontend/packages/ui/provider/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { QueryClientProvider } from "@tanstack/react-query";
import { queryClient } from "../libs/react-query";
import { CunninghamProvider } from "@openfun/cunningham-react";
import { FiltersProvider } from "../contexts/filtersContext";

type AppProviderProps = {
children: React.ReactNode;
};

const AppProvider = ({ children }: AppProviderProps) => (
<CunninghamProvider>
<FiltersProvider>
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools initialIsOpen />
</QueryClientProvider>
</FiltersProvider>
</CunninghamProvider>
);
export default AppProvider;
Loading