Skip to content

Commit

Permalink
add support for category comparison view
Browse files Browse the repository at this point in the history
  • Loading branch information
jekrch committed Jul 4, 2024
1 parent 2c840fe commit 27b0dbf
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 45 deletions.
61 changes: 41 additions & 20 deletions src/components/modals/config/DisplayTab.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Dispatch, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from '../../../redux/types';
import { setTheme, setVote, setContestants } from '../../../redux/actions';
import { setTheme, setVote, setContestants, setShowComparison } from '../../../redux/actions';
import { assignVotesByCode, updateVoteTypeCode, voteCodeHasType } from '../../../utilities/VoteProcessor';
import { countries } from '../../../data/Countries';
import Dropdown from '../../Dropdown';
Expand All @@ -12,6 +12,7 @@ const DisplayTab: React.FC = () => {
const dispatch: Dispatch<any> = useDispatch();
const vote = useSelector((state: AppState) => state.vote);
const theme = useSelector((state: AppState) => state.theme);
const showComparison = useSelector((state: AppState) => state.showComparison);
const contestants = useSelector((state: AppState) => state.contestants);

const year = useSelector((state: AppState) => state.year);
Expand All @@ -34,7 +35,6 @@ const DisplayTab: React.FC = () => {
return countries.filter((c) => c.key === sourceCountryKey)?.[0]?.name;
};


const [displayVoteSource, setDisplayVoteSource] = useState(() => getVoteSourceOption(vote));
const [voteDisplaySourceOptions, setVoteDisplaySourceOptions] = useState<string[]>([
'All',
Expand Down Expand Up @@ -72,6 +72,17 @@ const DisplayTab: React.FC = () => {
dispatch(setVote(newVote));
updateQueryParams({ v: newVote });
}
};

// Handle vote type input change
const onShowComparisonChange = (checked: boolean) => {

updateQueryParams({cm: checked === true ? 't' : 'f'})
dispatch(
setShowComparison(checked === true)
);


};

// Get vote source code from option
Expand Down Expand Up @@ -119,11 +130,10 @@ const DisplayTab: React.FC = () => {

return (
<div className="mb-0">
<div>
<h4 className="font-bold mb-[0.2em]">Show Votes</h4>

<div className="mb-[0.5em] border-slate-700 border-y-[1px]">
<div>
<div className="mb-[0.5em] border-slate-700 border-b-[1px] pb-2 -mt-2">
<span className="flex items-center ml-2">
<span className="ml-3 text-sm font-semibold">Show Votes:</span>
<Checkbox
id="total-checkbox"
checked={voteCodeHasType(vote, 't')}
Expand All @@ -149,10 +159,10 @@ const DisplayTab: React.FC = () => {
</span>

<div className="mt-[0.5em]">
<span className="ml-5 text-sm">{'From'}</span>
<span className="ml-5 text-sm font-semibold">{'From:'}</span>
<Dropdown
key="country-selector-2"
className="z-50 ml-4 min-w[6em] mx-auto mb-2"
className="z-50 ml-5 min-w[6em] mx-auto mb-2"
menuClassName="w-auto"
value={displayVoteSource}
onChange={(s) => setDisplayVoteSource(s)}
Expand All @@ -164,18 +174,29 @@ const DisplayTab: React.FC = () => {
</div>

<div>
<h4 className="font-bold mb-[0.7em] mt-[0em]">Theme</h4>

<div className="">
<Dropdown
key="theme-selector"
className="ml-5 z-50 w-30 mx-auto mb-3"
menuClassName=""
value={themeSelection}
onChange={(v) => onThemeInputChanged(v)}
options={['None', 'Auroral']}
showSearch={false}
/>

<div className="mt-4">
<div>
<div className="mb-2">
<Checkbox
id="total-checkbox"
className="ml-2"
checked={showComparison}
onChange={(c) => onShowComparisonChange(c)}
label="Show Category Comparisons"
/>
</div>
<span className="ml-5 font-semibold text-sm mb-[0.7em]">Theme:</span>
<Dropdown
key="theme-selector"
className="ml-5 z-50 w-30 mx-auto mb-3"
menuClassName=""
value={themeSelection}
onChange={(v) => onThemeInputChanged(v)}
options={['None', 'Auroral']}
showSearch={false}
/>
</div>
</div>
</div>
</div>
Expand Down
16 changes: 11 additions & 5 deletions src/components/ranking/DetailsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export interface DetailsCardProps {
export const DetailsCard: FC<DetailsCardProps> = (props) => {
const vote = useSelector((state: AppState) => state.vote);
const categories = useSelector((state: AppState) => state.categories);
const activeCategory = useSelector((state: AppState) => state.activeCategory);
const showTotalRank = useSelector((state: AppState) => state.showTotalRank);
const showComparison = useSelector((state: AppState) => state.showComparison);
const contestant = props.countryContestant.contestant;
const country = props.countryContestant.country;
const categoryRankingsRef = useRef<HTMLDivElement>(null);
Expand All @@ -42,7 +44,7 @@ export const DetailsCard: FC<DetailsCardProps> = (props) => {
}, [props.categoryScrollPosition]);

function getCategoryRankings() {
if (!showTotalRank) return undefined;
if (!showTotalRank && !showComparison) return undefined;

return getCountryCategoryRankingsFromUrl(categories, country);
}
Expand Down Expand Up @@ -177,7 +179,7 @@ export const DetailsCard: FC<DetailsCardProps> = (props) => {
}

</div>
{showTotalRank && (
{categories?.length && (showTotalRank || showComparison) && (
<div
ref={categoryRankingsRef}
className="mt-0 mx-[0.6em] shadow-lg rounded-b-md bg-[#1c214c] bg-opacity-100 border-gray-600 border-x-[0.01em] border-b-[0.01em] overflow-x-auto relative"
Expand All @@ -186,10 +188,14 @@ export const DetailsCard: FC<DetailsCardProps> = (props) => {
<div className="flex">
{categories.map((category, index) => {

const categoryRankName = categoryRankings?.[category.name];
if (!showTotalRank && index === activeCategory) {
return;
}

const categoryRankIndex = categoryRankings?.[category.name];

var { arrowIcon, rankDifference } = getRankIconaAndDiff(
props.rank, categoryRankName
props.rank, categoryRankIndex
);

return (
Expand All @@ -199,7 +205,7 @@ export const DetailsCard: FC<DetailsCardProps> = (props) => {
title={`weight: ${category.weight}`}
>
<span className="">{category.name}:</span>{' '}
<span className="ml-1 font-medium text-slate-300">{categoryRankName || 'N/A'}</span>
<span className="ml-1 font-medium text-slate-300">{categoryRankIndex || 'N/A'}</span>
{arrowIcon &&
<FontAwesomeIcon
icon={arrowIcon}
Expand Down
7 changes: 6 additions & 1 deletion src/redux/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Category } from "../utilities/CategoryUtil";
import { CountryContestant } from '../data/CountryContestant';
import { SetNameAction, SetYearAction, SetRankedItemsAction, SetUnrankedItemsAction, SetShowUnrankedAction, SetContestantsAction, SetIsDeleteModeAction, SetThemeAction, SetVoteAction, SetHeaderMenuOpen, SetCategories, SetActiveCategory, SetShowTotalRank } from './types';
import { SetNameAction, SetYearAction, SetRankedItemsAction, SetUnrankedItemsAction, SetShowUnrankedAction, SetContestantsAction, SetIsDeleteModeAction, SetThemeAction, SetVoteAction, SetHeaderMenuOpen, SetCategories, SetActiveCategory, SetShowTotalRank, SetShowComparison } from './types';

export const SET_NAME = 'SET_NAME';
export const SET_YEAR = 'SET_YEAR';
Expand All @@ -15,6 +15,7 @@ export const SET_UNRANKED_ITEMS = 'SET_UNRANKED_ITEMS';
export const SET_CATEGORIES = 'SET_CATEGORIES';
export const SET_ACTIVE_CATEGORY = 'SET_ACTIVE_CATEGORY';
export const SET_SHOW_TOTAL_RANK = 'SHOW_TOTAL_RANK'
export const SET_SHOW_COMPARISON = 'SHOW_COMPARISON';

export const setName = (name: string): SetNameAction => (
{ type: SET_NAME, payload: name }
Expand Down Expand Up @@ -66,4 +67,8 @@ export const setActiveCategory = (index: number | undefined): SetActiveCategory

export const setShowTotalRank = (show: boolean): SetShowTotalRank => (
{ type: SET_SHOW_TOTAL_RANK, payload: show }
);

export const setShowComparison = (show: boolean): SetShowComparison => (
{ type: SET_SHOW_COMPARISON, payload: show }
);
7 changes: 5 additions & 2 deletions src/redux/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Category } from "../utilities/CategoryUtil";
import { CountryContestant } from '../data/CountryContestant';
import { SET_NAME, SET_YEAR, SET_RANKED_ITEMS, SET_UNRANKED_ITEMS, SET_SHOW_UNRANKED, SET_CONTESTANTS, SET_IS_DELETE_MODE, SET_THEME, SET_VOTE, SET_HEADER_MENU_OPEN, SET_CATEGORIES, SET_ACTIVE_CATEGORY, SET_SHOW_TOTAL_RANK } from './actions';
import { SET_NAME, SET_YEAR, SET_RANKED_ITEMS, SET_UNRANKED_ITEMS, SET_SHOW_UNRANKED, SET_CONTESTANTS, SET_IS_DELETE_MODE, SET_THEME, SET_VOTE, SET_HEADER_MENU_OPEN, SET_CATEGORIES, SET_ACTIVE_CATEGORY, SET_SHOW_TOTAL_RANK, SET_SHOW_COMPARISON } from './actions';
import { Action, AppState } from './types';

const initialState: AppState = {
Expand All @@ -16,7 +16,8 @@ const initialState: AppState = {
unrankedItems: [],
categories: [],
activeCategory: undefined,
showTotalRank: false
showTotalRank: false,
showComparison: false
};

const rootReducer = (state = initialState, action: Action): AppState => {
Expand Down Expand Up @@ -47,6 +48,8 @@ const rootReducer = (state = initialState, action: Action): AppState => {
return { ...state, activeCategory: action.payload as number | undefined };
case SET_SHOW_TOTAL_RANK:
return { ...state, showTotalRank: action.payload as boolean };
case SET_SHOW_COMPARISON:
return { ...state, showComparison: action.payload as boolean };
default:
return state;
}
Expand Down
6 changes: 6 additions & 0 deletions src/redux/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AppState {
categories: Category[];
activeCategory: number | undefined;
showTotalRank: boolean;
showComparison: boolean;
theme: string;
vote: string;
}
Expand Down Expand Up @@ -85,4 +86,9 @@ export interface AppState {
export interface SetShowTotalRank extends Action {
type: 'SHOW_TOTAL_RANK';
payload: boolean;
}

export interface SetShowComparison extends Action {
type: 'SHOW_COMPARISON';
payload: boolean;
}
42 changes: 25 additions & 17 deletions src/utilities/UrlUtil.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
import { Dispatch } from 'redux';
import { setName, setYear, setRankedItems, setUnrankedItems, setContestants, setTheme, setVote } from '../redux/actions';
import { setName, setYear, setRankedItems, setUnrankedItems, setContestants, setTheme, setVote, setShowComparison } from '../redux/actions';
import { fetchCountryContestantsByYear } from './ContestantRepository';
import { CountryContestant } from '../data/CountryContestant';
import { countries } from '../data/Countries';
import { defaultYear, sanitizeYear } from '../data/Contestants';
import { getRankingComparison } from './RankAnalyzer';

import { Category } from './CategoryUtil';

export type UrlParams = {
rankingName: string | null; // n
contestYear: string | null; // y
rankings: string | null; // r
theme: string | null; // t: ab
voteCode: string | null; // v: {round}-{type}-{fromCountryKey} f-t-gb
comparisonMode: string | null; // cm: t/f
}

/**
* Updates states based on extracted parameters using Redux.
*/
export const updateStates = (
params: {
rankingName: string | null,
contestYear: string | null,
theme: string | null,
voteCode: string | null
},
params: UrlParams,
dispatch: Dispatch<any>
) => {
let { rankingName, contestYear, theme, voteCode } = params;
let { rankingName, contestYear, theme, voteCode, comparisonMode } = params;

if (rankingName) {
dispatch(
setName(rankingName)
);
}

dispatch(
setShowComparison(comparisonMode === 't')

Check failure on line 35 in src/utilities/UrlUtil.ts

View workflow job for this annotation

GitHub Actions / test (16.x)

src/utilities/UrlUtil.test.ts > updateStates > should dispatch setName when rankingName is provided

Error: [vitest] No "setShowComparison" export is defined on the "../redux/actions" mock. Did you forget to return it from "vi.mock"? If you need to partially mock a module, you can use "importOriginal" helper inside: vi.mock("../redux/actions", async (importOriginal) => { const actual = await importOriginal() return { ...actual, // your mocked methods } }) ❯ Module.updateStates src/utilities/UrlUtil.ts:35:9 ❯ src/utilities/UrlUtil.test.ts:106:9

Check failure on line 35 in src/utilities/UrlUtil.ts

View workflow job for this annotation

GitHub Actions / test (16.x)

src/utilities/UrlUtil.test.ts > updateStates > should dispatch setYear with defaultYear when contestYear is not provided

Error: [vitest] No "setShowComparison" export is defined on the "../redux/actions" mock. Did you forget to return it from "vi.mock"? If you need to partially mock a module, you can use "importOriginal" helper inside: vi.mock("../redux/actions", async (importOriginal) => { const actual = await importOriginal() return { ...actual, // your mocked methods } }) ❯ Module.updateStates src/utilities/UrlUtil.ts:35:9 ❯ src/utilities/UrlUtil.test.ts:112:9

Check failure on line 35 in src/utilities/UrlUtil.ts

View workflow job for this annotation

GitHub Actions / test (16.x)

src/utilities/UrlUtil.test.ts > updateStates > should dispatch setTheme and setVote with empty strings when they are not provided

Error: [vitest] No "setShowComparison" export is defined on the "../redux/actions" mock. Did you forget to return it from "vi.mock"? If you need to partially mock a module, you can use "importOriginal" helper inside: vi.mock("../redux/actions", async (importOriginal) => { const actual = await importOriginal() return { ...actual, // your mocked methods } }) ❯ Module.updateStates src/utilities/UrlUtil.ts:35:9 ❯ src/utilities/UrlUtil.test.ts:118:9
);

dispatch(
setTheme(theme ?? "")
);
Expand Down Expand Up @@ -149,7 +157,7 @@ export async function decodeRankingsFromURL(
dispatch: Dispatch<any>
): Promise<string[] | undefined> {

const extractedParams = getUrlParams(activeCategory);
const extractedParams: UrlParams = getUrlParams(activeCategory);

// console.log(activeCategory)
// console.log(window.location.search)
Expand All @@ -165,10 +173,9 @@ export async function decodeRankingsFromURL(
);
};

export function getUrlParams(activeCategory: number | undefined) {
export function getUrlParams(activeCategory: number | undefined): UrlParams {
const params = new URLSearchParams(window.location.search);
const extractedParams = extractParams(params, activeCategory);
return extractedParams;
return extractParams(params, activeCategory);
}

export function getUrlParam(paramName: string): string | null {
Expand Down Expand Up @@ -246,14 +253,15 @@ export function clearAllRankingParams(categories: Category[]) {
window.history.replaceState(null, '', newUrl);
}

export const extractParams = (params: URLSearchParams, activeCategory: number | undefined) => {
export const extractParams = (params: URLSearchParams, activeCategory: number | undefined): UrlParams => {
return {
rankingName: params.get('n'),
contestYear: params.get('y'),
rankings: params.get(`r${activeCategory !== undefined ? activeCategory + 1 : ''}`),
theme: params.get('t'), // e.g. ab
voteCode: params.get('v') // e.g. {round}-{type}-{fromCountryKey} f-t-gb
};
theme: params.get('t'), // e.g. ab
voteCode: params.get('v'), // e.g. {round}-{type}-{fromCountryKey} f-t-gb
comparisonMode: params.get('cm') // e.g. t/f
} as UrlParams;
};

export const updateUrlFromRankedItems = async (
Expand Down

0 comments on commit 27b0dbf

Please sign in to comment.