- #21: Add missing await in removeChoiceIfPresent() to prevent user appearing in two restaurants - #15: Add 1-hour TTL for menu refetching to avoid scraping on every page load - #9: Block stats API and UI navigation for future dates - #14: Add restaurant warnings (missing soup/prices, stale data) with warning icon - #12: Pre-fill restaurant/departure dropdowns from existing choices on page refresh - #10: Add voting statistics endpoint and table on stats page
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
import { FeatureRequest, VotingStats } from "../../types/gen/types.gen";
|
|
import getStorage from "./storage";
|
|
|
|
interface VotingData {
|
|
[login: string]: FeatureRequest[],
|
|
}
|
|
|
|
export interface VotingStatsResult {
|
|
[feature: string]: number;
|
|
}
|
|
|
|
const storage = getStorage();
|
|
const STORAGE_KEY = 'voting';
|
|
|
|
/**
|
|
* Vrátí pole voleb, pro které uživatel aktuálně hlasuje.
|
|
*
|
|
* @param login login uživatele
|
|
* @returns pole voleb
|
|
*/
|
|
export async function getUserVotes(login: string) {
|
|
const data = await storage.getData<VotingData>(STORAGE_KEY);
|
|
return data?.[login] || [];
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje hlas uživatele pro konkrétní volbu.
|
|
*
|
|
* @param login login uživatele
|
|
* @param option volba
|
|
* @param active příznak, zda volbu přidat nebo odebrat
|
|
* @returns aktuální data
|
|
*/
|
|
export async function updateFeatureVote(login: string, option: FeatureRequest, active: boolean): Promise<VotingData> {
|
|
let data = await storage.getData<VotingData>(STORAGE_KEY);
|
|
data ??= {};
|
|
if (!(login in data)) {
|
|
data[login] = [];
|
|
}
|
|
const index = data[login].indexOf(option);
|
|
if (index > -1) {
|
|
if (active) {
|
|
throw Error('Pro tuto možnost jste již hlasovali');
|
|
} else {
|
|
data[login].splice(index, 1);
|
|
if (data[login].length === 0) {
|
|
delete data[login];
|
|
}
|
|
}
|
|
} else if (active) {
|
|
if (data[login].length == 4) {
|
|
throw Error('Je možné hlasovat pro maximálně 4 možnosti');
|
|
}
|
|
data[login].push(option);
|
|
}
|
|
await storage.setData(STORAGE_KEY, data);
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Vrátí agregované statistiky hlasování - počet hlasů pro každou funkci.
|
|
*
|
|
* @returns objekt, kde klíčem je název funkce a hodnotou počet hlasů
|
|
*/
|
|
export async function getVotingStats(): Promise<VotingStatsResult> {
|
|
const data = await storage.getData<VotingData>(STORAGE_KEY);
|
|
const stats: VotingStatsResult = {};
|
|
if (data) {
|
|
for (const votes of Object.values(data)) {
|
|
for (const feature of votes) {
|
|
stats[feature] = (stats[feature] || 0) + 1;
|
|
}
|
|
}
|
|
}
|
|
return stats;
|
|
} |