diff --git a/.prettierrc b/.prettierrc index a77fdde..df17915 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,7 +2,7 @@ "useTabs": true, "singleQuote": true, "trailingComma": "none", - "printWidth": 100, + "printWidth": 160, "plugins": ["prettier-plugin-svelte"], "pluginSearchDirs": ["."], "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] diff --git a/src/app.d.ts b/src/app.d.ts index f59b884..cdf6f6d 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -6,6 +6,9 @@ declare global { // interface Locals {} // interface PageData {} // interface Platform {} + interface Locals { + authenticated: boolean | null + } } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..3d81f50 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,13 @@ +import { redirect } from "@sveltejs/kit"; + +export async function handle({ event, resolve }) { + event.locals.authenticated = true; + + if (event.url.pathname.startsWith('/admin')) { + if (!event.locals.authenticated) { + throw redirect(303, '/401'); + } + } + + return await resolve(event); +} \ No newline at end of file diff --git a/src/lib/stores/GameGroupFilter.ts b/src/lib/stores/GameGroupFilter.ts index c4001cc..ce6062f 100644 --- a/src/lib/stores/GameGroupFilter.ts +++ b/src/lib/stores/GameGroupFilter.ts @@ -1,5 +1,10 @@ import { writable, type Writable } from "svelte/store"; -const SelectedGameGroupsStore: Writable>> = writable(new Map()); +export type SelectedChellarisGroups = { + gameId: number, + selectedGroups: Array, +} + +const SelectedGameGroupsStore: Writable> = writable([]); // Key should always be the same as gameId export default SelectedGameGroupsStore; \ No newline at end of file diff --git a/src/lib/types/chellaris.ts b/src/lib/types/chellaris.ts index 0b76c7f..e9837ae 100644 --- a/src/lib/types/chellaris.ts +++ b/src/lib/types/chellaris.ts @@ -1,35 +1,38 @@ import type { Ethic, Species } from './stellaris'; export interface ChellarisInfo { - games: Map, // Key is Game Id - ethics: Map, // Key is Ethic Id - species: Map, // Key is Species Group Id + games: Array, // Key is Game Id + ethics: Array, // Key is Ethic Id + species: Array, // Key is Species Group Id } export type ChellarisGame = { + id: number, name: string, - groups: Map, // Key is Group Id - empires: Map, // Key is Empire Id + groups: Array, // Key is Group Id + empires: Array, // Key is Empire Id } export type ChellarisGameGroup = { + id: number, name: string, } export type ChellarisEmpire = { + id: number, gestalt: boolean, machine: boolean, group: number, empire_portrait: number, // TODO replace with Enum? empire_portrait_group: number, // TODO replace with Enum? discord_user?: string, - ethics: Map, + ethics: Array, } export const createChellarisInfo = (): ChellarisInfo => { const newChellarisInfo = { - games: new Map(), - ethics: new Map(), - species: new Map(), + games: [], + ethics: [], + species: [], }; return newChellarisInfo; @@ -37,9 +40,10 @@ export const createChellarisInfo = (): ChellarisInfo => { export const createChellarisGame = (): ChellarisGame => { const newChellarisGame = { + id: 0, name: "", - groups: new Map(), - empires: new Map(), + groups: [], + empires: [], }; return newChellarisGame; @@ -47,6 +51,7 @@ export const createChellarisGame = (): ChellarisGame => { export const createChellarisGameGroup = (): ChellarisGameGroup => { const newChellarisGameGroup = { + id: 0, name: "", }; @@ -55,8 +60,9 @@ export const createChellarisGameGroup = (): ChellarisGameGroup => { export const createChellarisEmpire = ( { - discord_user, group_id, gestalt, empire_portrait_id, empire_portrait_group_id + id, discord_user, group_id, gestalt, empire_portrait_id, empire_portrait_group_id }: { + id: number, discord_user?: string, group_id: number, gestalt: boolean, @@ -64,12 +70,13 @@ export const createChellarisEmpire = ( empire_portrait_group_id: number, }): ChellarisEmpire => { const newChellarisEmpire = { + id: id, gestalt: gestalt, machine: false, group: group_id, empire_portrait: empire_portrait_id, empire_portrait_group: empire_portrait_group_id, - ethics: new Map(), + ethics: [], discord_user: discord_user, }; diff --git a/src/lib/types/stellaris.ts b/src/lib/types/stellaris.ts index d890832..4702000 100644 --- a/src/lib/types/stellaris.ts +++ b/src/lib/types/stellaris.ts @@ -65,17 +65,20 @@ export class EthicsDataLegacy { } export type Ethic = { + id: number, displayName: string, machine: boolean, fanatic?: boolean, } export type Species = { + id: number, displayName: string, - portraits: Map, // Key is Portrait Id + portraits: Array, // Key is Portrait Id } export type Portrait = { + id: number, hires: string, lores: string, } diff --git a/src/routes/401/+page.svelte b/src/routes/401/+page.svelte new file mode 100644 index 0000000..d5e78bb --- /dev/null +++ b/src/routes/401/+page.svelte @@ -0,0 +1,12 @@ + + About + + + +
+

Unauthorized

+ +

+ Your request was denied, the authentication token in your settings seems to be invalid. +

+
diff --git a/src/routes/Header.svelte b/src/routes/Header.svelte index 904ee7b..f63e32f 100644 --- a/src/routes/Header.svelte +++ b/src/routes/Header.svelte @@ -21,9 +21,9 @@
  • Game 15 Graphs
  • -
  • +
  • Admin Menu
  • diff --git a/src/routes/graphs/+layout.ts b/src/routes/graphs/+layout.ts index a4c4009..d103b55 100644 --- a/src/routes/graphs/+layout.ts +++ b/src/routes/graphs/+layout.ts @@ -1,6 +1,6 @@ import ChellarisDataStore from '$lib/stores/ChellarisData'; import SelectedGameStore from '$lib/stores/GameFilter'; -import SelectedGameGroupsStore from "$lib/stores/GameGroupFilter"; +import SelectedGameGroupsStore, { type SelectedChellarisGroups } from "$lib/stores/GameGroupFilter"; import GraphsTabStore from '$lib/stores/GraphsTab'; import { createChellarisInfo, type ChellarisGame, createChellarisGame, createChellarisGameGroup, createChellarisEmpire } from "$lib/types/chellaris"; import type { LayoutLoad } from "./$types"; @@ -10,79 +10,82 @@ export const load: LayoutLoad = async ({ fetch }) => { let store: string | null; const apiBaseUrl = 'https://www.chellaris.net/api/v2'; const chellarisData = createChellarisInfo(); - + // Chellaris Data Code - const games: {id: number, name: string}[] = await (await fetch(apiBaseUrl + '/games')).json(); + const games: { id: number, name: string }[] = await (await fetch(apiBaseUrl + '/games')).json(); games.sort((a, b) => (a.name < b.name ? -1 : 1)); games.forEach(game => { const newGame: ChellarisGame = createChellarisGame(); + newGame.id = game.id; newGame.name = game.name; - chellarisData.games.set(game.id, newGame); + chellarisData.games[game.id] = newGame; }); - const groups: {id: number, name: string, game_id: number}[] = await (await fetch(apiBaseUrl + '/game_groups')).json(); + const groups: { id: number, name: string, game_id: number }[] = await (await fetch(apiBaseUrl + '/game_groups')).json(); groups.sort((a, b) => (a.name < b.name ? -1 : 1)); groups.forEach(group => { - const gameData = chellarisData.games.get(group.game_id); + const gameData = chellarisData.games[group.game_id]; if (typeof gameData !== "undefined") { const newGroup = createChellarisGameGroup(); + newGroup.id = group.id; newGroup.name = group.name; - gameData.groups.set(group.id, newGroup) - chellarisData.games.set(group.game_id, gameData); + chellarisData.games[group.game_id].groups[group.id] = newGroup; } }) const empires: { - id: number, - discord_user?: string, + id: number, + discord_user?: string, group_id: number, gestalt: boolean, - empire_portrait_id: number, - empire_portrait_group_id: number, - group_game_id: number}[] = await (await fetch(apiBaseUrl + '/empires')).json(); + empire_portrait_id: number, + empire_portrait_group_id: number, + group_game_id: number + }[] = await (await fetch(apiBaseUrl + '/empires')).json(); empires.sort((a, b) => (a.id < b.id ? -1 : 1)); empires.forEach(empire => { - const gameData = chellarisData.games.get(empire.group_game_id); + const gameData = chellarisData.games[empire.group_game_id]; if (typeof gameData !== "undefined") { const newEmpire = createChellarisEmpire(empire); - gameData.empires.set(empire.id, newEmpire); + chellarisData.games[empire.group_game_id].empires[empire.id] = newEmpire; } }); - const ethics: {id: number, name: string, machine_ethic: boolean}[] = await (await fetch(apiBaseUrl + '/ethics')).json(); - + const ethics: { id: number, name: string, machine_ethic: boolean }[] = await (await fetch(apiBaseUrl + '/ethics')).json(); + ethics.sort((a, b) => (a.id < b.id ? -1 : 1)); ethics.forEach(ethic => { - const newEthic: Ethic = {displayName: ethic.name, machine: ethic.machine_ethic}; + const newEthic: Ethic = { id: ethic.id, displayName: ethic.name, machine: ethic.machine_ethic }; - chellarisData.ethics.set(ethic.id, newEthic); - }); + chellarisData.ethics[ethic.id] = newEthic; + }); const empireEthics: { - empires_id: number, - empires_group_id: number, - empires_group_game_id: number, + empires_id: number, + empires_group_id: number, + empires_group_game_id: number, ethics_id: number, - ethics_fanatic: boolean}[] = await (await fetch(apiBaseUrl + '/empire_ethics')).json(); + ethics_fanatic: boolean + }[] = await (await fetch(apiBaseUrl + '/empire_ethics')).json(); empireEthics.forEach(empireEthic => { - const gameData = chellarisData.games.get(empireEthic.empires_group_game_id); - const ethic = chellarisData.ethics.get(empireEthic.ethics_id); + const gameData = chellarisData.games[empireEthic.empires_group_game_id]; + const ethic = chellarisData.ethics[empireEthic.ethics_id]; if (typeof gameData !== "undefined" && typeof ethic !== "undefined") { - const empireData = gameData.empires.get(empireEthic.empires_id); + const empireData = gameData.empires[empireEthic.empires_id]; if (typeof empireData !== "undefined") { - const tmpEthic: Ethic = {machine: ethic.machine, displayName: ethic.displayName, fanatic: empireEthic.ethics_fanatic}; + const tmpEthic: Ethic = { id: ethic.id, machine: ethic.machine, displayName: ethic.displayName, fanatic: empireEthic.ethics_fanatic }; if (tmpEthic.machine) { empireData.machine = true; } - empireData.ethics.set(empireEthic.ethics_id, tmpEthic); + empireData.ethics[empireEthic.ethics_id] = tmpEthic; } } }); @@ -95,70 +98,95 @@ export const load: LayoutLoad = async ({ fetch }) => { portraitGroups.sort((a, b) => (a.id < b.id ? -1 : 1)); portraitGroups.forEach(portraitGroup => { - const newPortraitGroup = { displayName: portraitGroup.name, portraits: new Map() }; - chellarisData.species.set(portraitGroup.id, newPortraitGroup); + const newPortraitGroup = { id: portraitGroup.id, displayName: portraitGroup.name, portraits: [] }; + chellarisData.species[portraitGroup.id] = newPortraitGroup; }); - const portraits: { - id: number, + const portraits: { + id: number, hires: string, lores: string, - group_id: number + group_id: number }[] = await (await fetch(apiBaseUrl + '/portraits')).json(); portraits.sort((a, b) => (a.id < b.id ? -1 : 1)); portraits.forEach(portrait => { - const portraitGroupData = chellarisData.species.get(portrait.group_id); + const portraitGroupData = chellarisData.species[portrait.group_id]; if (typeof portraitGroupData !== "undefined") { - const newPortraitData = { hires: portrait.hires, lores: portrait.lores }; + const newPortraitData = { id: portrait.id, hires: portrait.hires, lores: portrait.lores }; - portraitGroupData.portraits.set(portrait.id, newPortraitData); + portraitGroupData.portraits[portrait.id] = newPortraitData; } }) ChellarisDataStore.set(chellarisData); // Local Storage Code + let gameGroupSelections: Array = []; + let gameSelection: number | undefined; + if (typeof localStorage !== 'undefined') { // Tab Selection store = localStorage.getItem('graphsTab'); - if (typeof store == 'string') { + if (typeof store === 'string') { GraphsTabStore.set(store); } + // Game Selection store = localStorage.getItem('gameSelection'); - let selectedGame = 1; - if (typeof store == 'string' && store != "\"\"") { - selectedGame = JSON.parse(store); + if (typeof store === 'string' && store != "\"\"") { + gameSelection = JSON.parse(store); + } + + if (typeof gameSelection === 'undefined') { + gameSelection = chellarisData.games[0].id; } - SelectedGameStore.set(selectedGame); // Game Groups Selection store = localStorage.getItem('gameGroupSelection'); - if (typeof store == 'string') { - let selectedGameGroups: Array = []; - const gameGroupSelectionMap = new Map>(JSON.parse(store)); - const tmp = gameGroupSelectionMap.get(selectedGame); - - if (typeof tmp !== 'undefined') { - selectedGameGroups = tmp; - } else { - const tmpGameData = chellarisData.games.get(selectedGame); - // If this fails an empty array is precisely what we want - if (typeof tmpGameData !== "undefined") { - // Default to all available groups - selectedGameGroups = [...tmpGameData.groups.keys()]; - // Set Local Storage to default Values if not previously defined - localStorage.setItem('gameGroupSelection', JSON.stringify(Array.from(selectedGameGroups.entries()))); - } + if (typeof store === 'string') { + gameGroupSelections = JSON.parse(store); + + if (typeof gameGroupSelections[gameSelection] === 'undefined') { + // Default to all available groups + gameGroupSelections[gameSelection] = { gameId: gameSelection, selectedGroups: chellarisData.games[gameSelection].groups.map(group => group.id) }; + + // Set Local Storage to default Values if not previously defined + localStorage.setItem('gameGroupSelection', JSON.stringify(gameGroupSelections)); + } + else { + Object.keys(gameGroupSelections).forEach( + gKey => { + if (gameGroupSelections[+gKey] == null) { + delete gameGroupSelections[+gKey]; + } + else { + Object.keys(gameGroupSelections[+gKey].selectedGroups).forEach( + key => gameGroupSelections[+gKey].selectedGroups[+key] != null || delete gameGroupSelections[+gKey].selectedGroups[+key] + ) + } + }); } - gameGroupSelectionMap.set(selectedGame, selectedGameGroups); - SelectedGameGroupsStore.set(gameGroupSelectionMap); } } + + if (typeof gameSelection === 'undefined') { + gameSelection = chellarisData.games[0].id; + } + + SelectedGameStore.set(gameSelection); + + if (typeof gameGroupSelections[gameSelection] === 'undefined') { + // Default to all available groups + gameGroupSelections[gameSelection] = { gameId: gameSelection, selectedGroups: chellarisData.games[gameSelection].groups.map(group => group.id) }; + } + + SelectedGameGroupsStore.set(gameGroupSelections); + + gameGroupSelections = []; // TODO: actually assing a value return { chellarisData } } \ No newline at end of file diff --git a/src/routes/graphs/GameGroupSelection.svelte b/src/routes/graphs/GameGroupSelection.svelte index 4e037c3..3246563 100644 --- a/src/routes/graphs/GameGroupSelection.svelte +++ b/src/routes/graphs/GameGroupSelection.svelte @@ -4,88 +4,54 @@ import SelectedGameGroupsStore from '$lib/stores/GameGroupFilter'; import SelectedGameStore from '$lib/stores/GameFilter'; import ChellarisDataStore from '$lib/stores/ChellarisData'; - import { createChellarisInfo, type ChellarisGame, type ChellarisGameGroup, type ChellarisInfo } from '$lib/types/chellaris'; - - let selectedGame: number; - let selectedGameGroups: Array = []; - let selectedGameGroupsMap: Map> = new Map(); - - let gameGroups: Map = new Map(); - let chellarisData: ChellarisInfo = createChellarisInfo(); - - // Chellaris Data Code - const updateGameGroups = () => { - let tmpData; - if (!selectedGame) { - tmpData = chellarisData.games.get(chellarisData.games.keys().next().value); - } - else { - tmpData = chellarisData.games.get(selectedGame); - } - - if (typeof tmpData !== 'undefined') { - gameGroups = tmpData.groups; - } - } - - ChellarisDataStore.subscribe((data) => { - chellarisData = data; - - updateGameGroups(); - // TODO Update selection if Groups Differ? Does this value ever even update without a full page reload? - }); - - // Group Selection Code - const updateSelectedGroups = () => { - const tmp = selectedGameGroupsMap.get(selectedGame); - if (typeof tmp !== 'undefined') { - selectedGameGroups = [...tmp.values()]; - } else { - selectedGameGroups = [...gameGroups.keys()]; - selectedGameGroupsMap.set(selectedGame, selectedGameGroups); - SelectedGameGroupsStore.set(selectedGameGroupsMap); - } - }; + // Game Selection Code SelectedGameStore.subscribe((selection) => { - selectedGame = selection; - - updateGameGroups(); - - if (selectedGameGroupsMap.size != 0) { - updateSelectedGroups(); + if (typeof $SelectedGameGroupsStore[selection] === 'undefined') { + // Default to all available groups + $SelectedGameGroupsStore[selection] = { gameId: $SelectedGameStore, selectedGroups: $ChellarisDataStore.games[$SelectedGameStore].groups.map((group) => group.id) }; + SelectedGameGroupsStore.update(() => $SelectedGameGroupsStore); } - }); - - SelectedGameGroupsStore.subscribe((selection) => { - selectedGameGroupsMap = selection; - updateSelectedGroups(); + }) + // Game Group Selection Code + $: { if (typeof localStorage !== 'undefined') { - localStorage.setItem('gameGroupSelection', JSON.stringify(Array.from(selection.entries()))); + localStorage.setItem('gameGroupSelection', JSON.stringify($SelectedGameGroupsStore)); } - }); + } - const updateGameGroupSelection = () => { - SelectedGameGroupsStore.update((selection) => selection.set(selectedGame, selectedGameGroups)); - }; + $: selectedGameData = $SelectedGameGroupsStore[$SelectedGameStore]; + $: groupJoiner = selectedGameData.selectedGroups.length > 2 ? ', ' : ' & '; + + const updateGroupStore = () => { + SelectedGameGroupsStore.update(() => $SelectedGameGroupsStore); + } const dropdownId = crypto.randomUUID(); - 1 ? "Groups " : "Group ") + selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(", ")}> - {#each gameGroups as group} - - - - + 1 ? 'Groups ' : 'Group ') + + selectedGameData.selectedGroups + .map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name) + .join(groupJoiner)} +> + {#each $ChellarisDataStore.games[$SelectedGameStore].groups as group} + {#if group} + + + + + {/if} {/each} diff --git a/src/routes/graphs/GameSelection.svelte b/src/routes/graphs/GameSelection.svelte index d37b48d..d4f6515 100644 --- a/src/routes/graphs/GameSelection.svelte +++ b/src/routes/graphs/GameSelection.svelte @@ -3,45 +3,29 @@ import DropDownElement from '$lib/components/DropDownElement.svelte'; import SelectedGameStore from '$lib/stores/GameFilter'; import ChellarisDataStore from '$lib/stores/ChellarisData'; - import type { ChellarisGame } from '$lib/types/chellaris'; - - let selectedGame: number; - let gameList: Map = new Map(); - - // Chellaris Data Code - ChellarisDataStore.subscribe((data) => { - gameList = data.games; - }); // Game Selection Code - SelectedGameStore.subscribe((selection) => { - selectedGame = selection; - + $: { if (typeof localStorage !== 'undefined') { - localStorage.setItem('gameSelection', JSON.stringify(selectedGame)); + localStorage.setItem('gameSelection', JSON.stringify($SelectedGameStore)); } - }); - - const updateGameSelection = () => { - SelectedGameStore.update(() => selectedGame); }; // Dropdown UUID to make the Click Event work properly const dropdownId = crypto.randomUUID(); - - {#each gameList as game} + + {#each $ChellarisDataStore.games as game} - + {/each} diff --git a/src/routes/graphs/SubNav.svelte b/src/routes/graphs/SubNav.svelte index 000289a..b66a6c4 100644 --- a/src/routes/graphs/SubNav.svelte +++ b/src/routes/graphs/SubNav.svelte @@ -12,12 +12,12 @@
      -
    • +
    • Excel Style
    • diff --git a/src/routes/graphs/excel-style/+page.svelte b/src/routes/graphs/excel-style/+page.svelte index 9e585aa..d02ca25 100644 --- a/src/routes/graphs/excel-style/+page.svelte +++ b/src/routes/graphs/excel-style/+page.svelte @@ -1,96 +1,88 @@ @@ -211,7 +186,7 @@ {#if pageData.init}

      {selectedGame.name} Sign-Up Info for {groupNoun} - {selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(groupJoiner)} + {selectedGameGroupData.selectedGroups.map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name).join(groupJoiner)}

      @@ -227,11 +202,11 @@ # Fanatic {#each pageData.ethicsData as ethicData} - {#if !ethicData[1].machine} + {#if ethicData && !ethicData.machine} - {ethicData[1].displayName} - {ethicData[1].regular} - {ethicData[1].fanatic} + {ethicData.displayName} + {ethicData.regular} + {ethicData.fanatic} {/if} {/each} @@ -247,10 +222,10 @@ # {#each pageData.ethicsData as ethicData} - {#if ethicData[1].machine} + {#if ethicData && ethicData.machine} - {ethicData[1].displayName} - {ethicData[1].regular} + {ethicData.displayName} + {ethicData.regular} {/if} {/each} @@ -266,37 +241,32 @@ {index + 1} {/each} - {#each chellarisData.species as portraitGroup} - - {portraitGroup[1].displayName} - {#each portraitGroup[1].portraits as portrait} - - - - - {/each} - + {#each $ChellarisDataStore.species ?? false as portraitGroup} + {#if portraitGroup} + + {portraitGroup.displayName} + {#each portraitGroup.portraits ?? false as portrait} + {#if portrait} + + + + + {/if} + {/each} + + {/if} {/each}
      diff --git a/src/routes/graphs/tab/+page.svelte b/src/routes/graphs/tab/+page.svelte index d8a3e3d..6d01c29 100644 --- a/src/routes/graphs/tab/+page.svelte +++ b/src/routes/graphs/tab/+page.svelte @@ -1,56 +1,14 @@ @@ -61,7 +19,7 @@

      Example Tab

      - {selectedGameGroups.length > 1 ? 'Groups' : 'Group'} - {selectedGameGroups.map((selection) => gameGroups.get(selection)?.name).join(', ')} - {selectedGameGroups.length > 1 ? 'are' : 'is'} selected + {selectedGroups.length > 1 ? 'Groups' : 'Group'} + {selectedGroups.map((selection) => $ChellarisDataStore.games[$SelectedGameStore].groups[selection]?.name).join(groupJoiner)} + {selectedGroups.length > 1 ? 'are' : 'is'} selected