diff --git a/packages/backend/package.json b/packages/backend/package.json index 6569518..5bcad5e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "docker build --progress=plain -t simrailpro:backend .", "rawbuild": "yarn tsc", - "start": "yarn build && doppler run node ../../dist/backend/index.js" + "start": "yarn rawbuild && node --env-file=.env dist/index.js" }, "author": "Aleksander Wilczyński", "license": "AGPL-3.0-only", diff --git a/packages/backend/src/http/server.ts b/packages/backend/src/http/server.ts index 52fe685..d743b0b 100644 --- a/packages/backend/src/http/server.ts +++ b/packages/backend/src/http/server.ts @@ -24,6 +24,7 @@ import { StatsRoute } from "./routes/stats.js"; import { LogRoute } from "./routes/log.js"; import { ActivePlayersRoute } from "./routes/activePlayer.js"; import { AdminRoute } from "./routes/admin.js"; +import { ImagesRoute } from './routes/images.js'; export class ApiModule { @@ -39,6 +40,8 @@ export class ApiModule router.use("/leaderboard/", LeaderboardRoute.load()); router.use("/active/", ActivePlayersRoute.load()); router.use("/admin/", AdminRoute.load()); + router.use("/images/", ImagesRoute.load()); + router.use("/stats/", StatsRoute.load()); router.use("/log/", LogRoute.load()); diff --git a/packages/backend/src/util/contants.ts b/packages/backend/src/util/contants.ts index 3d8e2c4..a9c594b 100644 --- a/packages/backend/src/util/contants.ts +++ b/packages/backend/src/util/contants.ts @@ -137,10 +137,80 @@ export const trainsList = [ "4E/EU07-*", ], }, + { + train: 'Ty2', + pattern: [ + 'Ty2/*' + ] + } ]; +export const stationsMap: Record = { + "Grodzisk Mazowiecki": "https://api.simrail.eu:8083/Thumbnails/Stations/gr1m.jpg", + "Korytów": "https://api.simrail.eu:8083/Thumbnails/Stations/kr1m.jpg", + "Szeligi": "https://api.simrail.eu:8083/Thumbnails/Stations/sz1m.jpg", + "Włoszczowa Północ": "https://api.simrail.eu:8083/Thumbnails/Stations/wp1m.jpg", + "Knapówka": "https://api.simrail.eu:8083/Thumbnails/Stations/kn1m.jpg", + "Psary": "https://api.simrail.eu:8083/Thumbnails/Stations/ps1m.jpg", + "Góra Włodowska": "https://api.simrail.eu:8083/Thumbnails/Stations/gw1m.jpg", + "Idzikowice": "https://api.simrail.eu:8083/Thumbnails/Stations/id1m.jpg", + "Katowice Zawodzie": "https://api.simrail.eu:8083/Thumbnails/Stations/kz1m.jpg", + "Sosnowiec Główny": "https://api.simrail.eu:8083/Thumbnails/Stations/sg1m.jpg", + "Dąbrowa Górnicza": "https://api.simrail.eu:8083/Thumbnails/Stations/dg1m.jpg", + "Zawiercie": "https://api.simrail.eu:8083/Thumbnails/Stations/zw1m.jpg", + "Będzin": "https://api.simrail.eu:8083/Thumbnails/Stations/b1m.jpg", + "Sosnowiec Południowy": "https://api.simrail.eu:8083/Thumbnails/Stations/spl1m.jpg", + "Opoczno Południe": "https://api.simrail.eu:8083/Thumbnails/Stations/op1m.jpg", + "Dąbrowa Górnicza Wschodnia": "https://api.simrail.eu:8083/Thumbnails/Stations/dws1m.jpg", + "Dorota": "https://api.simrail.eu:8083/Thumbnails/Stations/dra1m.jpg", + "Łazy Ła": "https://api.simrail.eu:8083/Thumbnails/Stations/la1m.jpg", + "Łazy": "https://api.simrail.eu:8083/Thumbnails/Stations/lb1m.jpg", + "Juliusz": "https://api.simrail.eu:8083/Thumbnails/Stations/ju1m.jpg", + "Łazy Łc": "https://api.simrail.eu:8083/Thumbnails/Stations/lc1m.jpg", + "Katowice": "https://api.simrail.eu:8083/Thumbnails/Stations/ko1m.jpg", + "Dąbrowa Górnicza Ząbkowice": "https://api.simrail.eu:8083/Thumbnails/Stations/dz1m.jpg", + "Sławków": "https://api.simrail.eu:8083/Thumbnails/Stations/sl1m.jpg", + "Starzyny": "https://api.simrail.eu:8083/Thumbnails/Stations/str1m.jpg", + "Bukowno": "https://api.simrail.eu:8083/Thumbnails/Stations/bo1m.jpg", + "Tunel": "https://api.simrail.eu:8083/Thumbnails/Stations/tl1m.jpg", + "Dąbrowa Górnicza Huta Katowice": "https://api.simrail.eu:8083/Thumbnails/Stations/dghk1m.jpg", + "Sosnowiec Kazimierz": "https://api.simrail.eu:8083/Thumbnails/Stations/skz1m.jpg", + "Pruszków": "https://api.simrail.eu:8083/Thumbnails/Stations/pr1m.jpg", + "Strzałki": "https://api.simrail.eu:8083/Thumbnails/Stations/st1m.jpg", + "Olszamowice": "https://api.simrail.eu:8083/Thumbnails/Stations/ol1m.jpg", + "Miechów": "https://api.simrail.eu:8083/Thumbnails/Stations/mi1m.jpg", + "Kraków Przedmieście": "https://api.simrail.eu:8083/Thumbnails/Stations/kpm1m.jpg", + "Kraków Batowice": "https://api.simrail.eu:8083/Thumbnails/Stations/kb1m.jpg", + "Raciborowice": "https://api.simrail.eu:8083/Thumbnails/Stations/ra1m.jpg", + "Zastów": "https://api.simrail.eu:8083/Thumbnails/Stations/zs1m.jpg", + "Niedźwiedź": "https://api.simrail.eu:8083/Thumbnails/Stations/nd1m.jpg", + "Słomniki": "https://api.simrail.eu:8083/Thumbnails/Stations/sm1m.jpg", + "Kozłów": "https://api.simrail.eu:8083/Thumbnails/Stations/koz1m.jpg", + "N/A": 'https://shared.steamstatic.com/store_item_assets/steam/apps/1422130/header.jpg' +}; + + + +export const trainsMap: Record = { + "Traxx (E186)": "https://wiki.simrail.eu/vehicle/poland/trains/elec-loco/traxx/20241029163359_1.jpg", + "Dragon2 (E6ACTa, E6ACTadb)": "https://wiki.simrail.eu/vehicle/e6acta-016.jpg", + "Dragon2 (ET25)": "https://wiki.simrail.eu/vehicle/et25-002.jpg", + "Pendolino (ED250)": "https://wiki.simrail.eu/vehicle/ed250-001.png", + "EN57": "https://wiki.simrail.eu/vehicle/en57-009.png", + "EN71": "https://wiki.simrail.eu/vehicle/en71-002.png", + "EN76": "https://wiki.simrail.eu/vehicle/en76-006.jpg", + "EN96": "https://wiki.simrail.eu/vehicle/en96-001.jpg", + "EP07": "https://wiki.simrail.eu/vehicle/ep07-174.jpg", + "EP08": "https://wiki.simrail.eu/vehicle/poland/trains/elec-loco/ep08/20241106002003_1.jpg", + "ET22": "https://wiki.simrail.eu/vehicle/et22-243.png", + "EU07": "https://wiki.simrail.eu/vehicle/eu07-005.jpg", + "Ty2": "https://wiki.simrail.eu/vehicle/ty2-70.png", + "N/A": 'https://shared.steamstatic.com/store_item_assets/steam/apps/1422130/header.jpg' +}; + export const getVehicle = (name: string) => { return trainsList.find(x => wcmatch(x.pattern)(name))?.train; -}; \ No newline at end of file +}; + diff --git a/packages/backend/src/util/imgproxy.ts b/packages/backend/src/util/imgproxy.ts index 32bac3b..a0f5e25 100644 --- a/packages/backend/src/util/imgproxy.ts +++ b/packages/backend/src/util/imgproxy.ts @@ -26,8 +26,10 @@ export const imgProxySign = (target: string) => return hmac.digest("base64url"); }; -export const generateUrl = (url: string, options: string = "rs:auto:128:128:1/f:png") => +export const generateUrl = (url: string, options?: string) => { + if (!options) options = "rs:auto:128:128:1/f:png"; + if (url.includes('https://proxy.cdn.alekswilc.dev/')) return url; if (process.env.NODE_ENV === "development") diff --git a/packages/frontend/src/components/mini/util/Paginator.tsx b/packages/frontend/src/components/mini/util/Paginator.tsx index 69d39eb..4d056e9 100644 --- a/packages/frontend/src/components/mini/util/Paginator.tsx +++ b/packages/frontend/src/components/mini/util/Paginator.tsx @@ -16,59 +16,58 @@ import { Dispatch, SetStateAction } from "react"; + +const getPaginationNums = (page: number, pages: number) => { + if (pages <= 5) + return Array.from({ length: pages }, (_, i) => i + 1); + + const numbers = [1]; + if (page <= 3) { + numbers.push(2, 3, 4); + } else if (page >= pages - 2) { + numbers.push(pages - 3, pages - 2, pages - 1); + } else { + numbers.push(page - 1, page, page + 1); + } + + numbers.push(pages); + return [...new Set(numbers)].sort((a, b) => a - b); +} + export const Paginator = ({ page, setPage, pages }: { page: number, pages: number, setPage: Dispatch> -}) => -{ - let numbers = [ 1, page - 2, page - 1, page, page + 1, page + 2 ]; - - page === 1 && (numbers = [ page, page + 1, page + 2, page + 3, page + 4 ]); - - page === 2 && (numbers = [ page - 1, page, page + 1, page + 2, page + 3 ]); - - page === 3 && (numbers = [ page - 2, page - 1, page, page + 1, page + 2 ]); - - (page === pages) && (numbers = [ 1, page - 4, page - 3, page - 2, page - 1 ]); - - (page === (pages - 1)) && (numbers = [ 1, page - 3, page - 2, page - 1, page ]); - - (page === (pages - 2)) && (numbers = [ 1, page - 2, page - 1, page, page + 1 ]); - - numbers = numbers.filter(x => (pages + 1) >= x && x > 0); +}) => { + // todo: rewrite this shit XDDDDDDDD + const numbers = getPaginationNums(page, pages); return
+ className="rounded-sm flex flex-row align-center justify-center p-2">
  • setPage(page => (page - 1) < 1 ? 1 : page - 1) }> + onClick={() => setPage(page => (page - 1) < 1 ? 1 : page - 1)}> + xmlns="http://www.w3.org/2000/svg"> + fill="">
  • - { numbers.map(num => - { + {numbers.map(num => { return
  • - setPage(num) } - className={ `cursor-pointer flex items-center justify-center border border-stroke border-l-transparent py-[5px] px-4 font-medium hover:border-primary hover:bg-gray hover:text-primary dark:border-strokedark dark:hover:border-primary dark:hover:bg-graydark select-none ${ page === num && "text-primary border-primary dark:border-primary" }` }>{ num } + setPage(num)} + className={`cursor-pointer flex items-center justify-center border border-stroke border-l-transparent py-[5px] px-4 font-medium hover:border-primary hover:bg-gray hover:text-primary dark:border-strokedark dark:hover:border-primary dark:hover:bg-graydark select-none ${page === num && "text-primary border-primary dark:border-primary"}`}>{num}
  • ; - }) } - - { !!pages &&
  • - setPage(pages) }>{ pages }
  • } + })}
  • setPage(page => (page + 1) > pages ? pages : page + 1) }> + onClick={() => setPage(page => (page + 1) > pages ? pages : page + 1)}> + xmlns="http://www.w3.org/2000/svg"> + fill=""> diff --git a/packages/frontend/src/components/pages/profiles/Profile.tsx b/packages/frontend/src/components/pages/profiles/Profile.tsx index b37e74c..7c7d5ae 100644 --- a/packages/frontend/src/components/pages/profiles/Profile.tsx +++ b/packages/frontend/src/components/pages/profiles/Profile.tsx @@ -15,9 +15,8 @@ */ import { useState } from "react"; -import { TProfileData } from "../../../types/profile.ts"; +import { TProfileData, TProfilePlayer } from "../../../types/profile.ts"; import { useTranslation } from "react-i18next"; -import { ArrowIcon, FlexArrowIcon } from "../../mini/icons/ArrowIcon.tsx"; import { formatTime } from "../../../util/time.ts"; import { useAuth } from "../../../hooks/useAuth.tsx"; import { ConfirmModal } from "../../mini/modal/ConfirmModal.tsx"; @@ -25,258 +24,253 @@ import { post } from "../../../util/fetcher.ts"; import { toast } from "react-toastify"; import dayjs from "dayjs"; import { UserIcons } from "../../mini/icons/UserIcons.tsx"; +import { StationStat } from '../../mini/profile/StationStat.tsx'; +import { chunk } from '../../../util/chunk.ts'; +import { Paginator } from '../../mini/util/Paginator.tsx'; +import { TrainStat } from '../../mini/profile/TrainStat.tsx'; +import { TImagesData } from '../../../types/images.ts'; -export const ProfileCard = ({ data }: { data: TProfileData }) => -{ +const sortTrainsByList: Record = { + [0]: 'time', + [1]: 'score', + [2]: 'distance', +} - const [ showTrains, setShowTrains ] = useState(false); - const [ showStations, setShowStations ] = useState(false); - const [ sortTrainsBy, setSortTrainsBy ] = useState<"time" | "score" | "distance">("distance"); - const [ hideLeaderboardStatsModal, setHideLeaderboardStatsModal ] = useState(false); - const [ hideProfileModal, setHideProfileModal ] = useState(false); +export const ProfileCard = ({ data, images }: { data: TProfileData, images: TImagesData }) => { + const [sortTrainsBy, setSortTrainsBy] = useState(0); + const [sortTrainsBy2, setSortTrainsBy2] = useState(2); + const [sortStationsBy, setSortStationsBy] = useState(0); + const [hideLeaderboardStatsModal, setHideLeaderboardStatsModal] = useState(false); + const [hideProfileModal, setHideProfileModal] = useState(false); const { isAdmin, token } = useAuth(); - const adminToggleHideLeaderboardPlayerProfile = () => - { - post(`/admin/profile/${ data.player.id }/${ data.player.flags.includes("leaderboard_hidden") ? "showLeaderboard" : "hideLeaderboard" }`, {}, { "X-Auth-Token": token }) - .then((response) => - { - if (response.code === 200) - { - toast.success(t("admin.hideLeaderboard.alert")); - } - }); + // #region ADMIN + const adminToggleHideLeaderboardPlayerProfile = () => { + post(`/admin/profile/${data.player.id}/${data.player.flags.includes("leaderboard_hidden") ? "showLeaderboard" : "hideLeaderboard"}`, {}, { "X-Auth-Token": token }) + .then((response) => { + if (response.code === 200) { + toast.success(t("admin.hideLeaderboard.alert")); + } + }); }; - const adminHidePlayerProfile = () => - { - post(`/admin/profile/${ data.player.id }/hide`, {}, { "X-Auth-Token": token }) - .then((response) => - { - if (response.code === 200) - { - toast.success(t("admin.hide.alert")); - } - }); + const adminHidePlayerProfile = () => { + post(`/admin/profile/${data.player.id}/hide`, {}, { "X-Auth-Token": token }) + .then((response) => { + if (response.code === 200) { + toast.success(t("admin.hide.alert")); + } + }); }; - const adminForceUpdate = () => - { - post(`/admin/profile/${ data.player.id }/forceUpdate`, {}, { "X-Auth-Token": token }) - .then((response) => - { - if (response.code === 200) - { - toast.success(t("admin.update.alert")); - } - }); + const adminForceUpdate = () => { + post(`/admin/profile/${data.player.id}/forceUpdate`, {}, { "X-Auth-Token": token }) + .then((response) => { + if (response.code === 200) { + toast.success(t("admin.update.alert")); + } + }); }; + // #endregion + const sortStations = (a: keyof TProfilePlayer['dispatcherStats'], b: keyof TProfilePlayer['dispatcherStats']) => { + if (sortStationsBy) { + const _a = a; + a = b; + b = _a; + } + + return data.player.dispatcherStats[b].time - data.player.dispatcherStats[a].time + } + + const sortTrains = (a: keyof TProfilePlayer['trainStats'], b: keyof TProfilePlayer['trainStats']) => { + if (sortTrainsBy2) { + const _a = a; + a = b; + b = _a; + } + + return data.player.trainStats[b][(sortTrainsByList[sortTrainsBy] ?? 'distance') as 'distance'] - data.player.trainStats[a][(sortTrainsByList[sortTrainsBy] ?? 'distance') as 'distance']; + } + + const dispatcherStats = [...chunk(Object.keys(data.player.dispatcherStats), 8)]; + const [dispatcherPage, setDispatcherPage] = useState(1); + + const trainStats = [...chunk(Object.keys(data.player.trainStats), 8)]; + const [trainPage, setTrainPage] = useState(1); + const { t } = useTranslation(); + + return <> - - + +
    + className="overflow-hidden ">
    + className="mx-auto max-w-44 rounded-full">
    - profile - { data.active && - } + profile + {data.active && + }

    - { data.player.username } + {data.player.username}

    + className="mx-auto mt-4.5 mb-5.5 grid max-w-94 grid-cols-2 rounded-md border border-stroke py-2.5 shadow-1 dark:border-strokedark dark:bg-[#37404F]">
    + className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row"> - { Math.floor(data.player.trainDistance / 1000) }km + {Math.floor(data.player.trainDistance / 1000)}km - { t("profile.stats.distance") } + {t("profile.stats.distance")}
    + className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row"> - { formatTime(data.player.dispatcherTime) } + {formatTime(data.player.dispatcherTime)} - { t("profile.stats.time") } + {t("profile.stats.time")}
    - { data.active && data.active.type === "train" && -
    -

    { t("profile.active.train", { train: `${ data.active.trainName } - ${ data.active.trainNumber }`, server: data.active.server.toUpperCase() }) }

    -
    } + {data.active && data.active.type === "train" && +
    +

    {t("profile.active.train", { train: `${data.active.trainName} - ${data.active.trainNumber}`, server: data.active.server.toUpperCase() })}

    +
    } - { data.active && data.active.type === "station" && -
    -

    { t("profile.active.station", { station: `${ data.active.stationName } - ${ data.active.stationShort }`, server: data.active.server.toUpperCase() }) }

    -
    } + {data.active && data.active.type === "station" && +
    +

    {t("profile.active.station", { station: `${data.active.stationName} - ${data.active.stationShort}`, server: data.active.server.toUpperCase() })}

    +
    }
    +
    +

    {t("profile.stations.header")}

    + +
    + {dispatcherStats[dispatcherPage - 1].sort(sortStations).map(stationName => { + const station = data.player.dispatcherStats[stationName]; - { Object.keys(data.player.trainStats || {}).length > 0 && -
    -
    setShowTrains(val => !val) }> -

    { t("profile.trains.header") }

    - + return + })} +
    +
    + +
    +
    + + + + {/*
    + +
    +
    +
    +
    + {t("profile.stations.station")} +
    - { showTrains && -
    -
    -
    -
    - { t("profile.trains.train") } -
    -
    -
    setSortTrainsBy("distance") }> -
    - { t("profile.trains.distance") } -
    - -
    -
    setSortTrainsBy("score") }> -
    - { t("profile.trains.points") } -
    - -
    - {/*
    setSortTrainsBy("time") }>*/ } - {/*
    */ } - {/* { t("profile.trains.time") }*/ } - {/*
    */ } - {/* */ } - {/*
    */ } -
    - - { Object.keys(data.player.trainStats).sort((a, b) => data.player.trainStats[ b ][ sortTrainsBy ] - data.player.trainStats[ a ][ sortTrainsBy ]).map(trainName => - { - const train = data.player.trainStats[ trainName ]; - - return
    -
    -

    - { trainName } -

    -
    - -
    -

    { Math.floor(train.distance / 1000) }km

    -
    - -
    -

    { train.score }

    -
    - - {/*
    */ } - {/*

    { formatTime(train.time) }

    */ } - {/*
    */ } -
    ; - }) } - - -
    } - -
    } - { Object.keys(data.player.dispatcherStats || {}).length > 0 && -
    -
    setShowStations(val => !val) }> -

    { t("profile.stations.header") }

    - +
    +
    + {t("profile.stations.time")} +
    - { showStations && -
    -
    -
    -
    - { t("profile.stations.station") } -
    -
    +
    + {Object.keys(data.player.dispatcherStats).sort((a, b) => data.player.dispatcherStats[b].time - data.player.dispatcherStats[a].time).map(stationName => { + const station = data.player.dispatcherStats[stationName]; + return
    +
    +

    + {stationName} +

    +
    -
    -
    - { t("profile.stations.time") } -
    -
    -
    - { Object.keys(data.player.dispatcherStats).sort((a, b) => data.player.dispatcherStats[ b ].time - data.player.dispatcherStats[ a ].time).map(stationName => - { - const station = data.player.dispatcherStats[ stationName ]; - return
    -
    -

    - { stationName } -

    -
    +
    +

    {formatTime(station.time)}

    +
    +
    ; + })} -
    -

    { formatTime(station.time) }

    -
    -
    ; - }) } +
    -
    } - -
    } - - - { isAdmin && <> -
    -

    { t("admin.header") }

    +
    */} + {isAdmin && <> +
    +

    {t("admin.header")}

    - { data.player.flags.includes("leaderboard_hidden") ? - : - } + {data.player.flags.includes("leaderboard_hidden") ? + : + }
    - } + } -
    +

    - { t("profile.info", { date: dayjs(data.player.createdAt).format("DD/MM/YYYY") }) } + {t("profile.info", { date: dayjs(data.player.createdAt).format("DD/MM/YYYY") })}

    diff --git a/packages/frontend/src/i18n/languages/cs.json b/packages/frontend/src/i18n/languages/cs.json index 845de68..645b941 100644 --- a/packages/frontend/src/i18n/languages/cs.json +++ b/packages/frontend/src/i18n/languages/cs.json @@ -63,14 +63,27 @@ "trains": { "header": "Statistiky vlaků", "train": "Vlak", - "distance": "Vzdálenost", - "points": "Body", - "time": "Čas" + "distance": "Vzdálenost: {{distance}}km", + "score": "Body: {{score}", + "time": "Čas: {{time}}", + "sortby": { + "title": "Řadit podle: ", + "min": "Nejnižší", + "max": "Nejvyšší", + "time": "Čas", + "distance": "Vzdálenost", + "score": "Body" + } }, "stations": { "header": "Statistiky stanic", "station": "Stanice", - "time": "Čas" + "time": "Čas: {{time}}", + "sortby": { + "title": "Řadit podle: ", + "min": "Nejnižší", + "max": "Nejvyšší" + } }, "errors": { "notfound": { diff --git a/packages/frontend/src/i18n/languages/en.json b/packages/frontend/src/i18n/languages/en.json index a1aca86..9a80f12 100644 --- a/packages/frontend/src/i18n/languages/en.json +++ b/packages/frontend/src/i18n/languages/en.json @@ -63,14 +63,27 @@ "trains": { "header": "Train Statistics", "train": "Train", - "distance": "Distance", - "points": "Points", - "time": "Time" + "distance": "Distance: {{distance}}km", + "score": "Points: {{score}}", + "time": "Time: {{time}}", + "sortby": { + "title": "Sort: ", + "min": "Lowest", + "max": "Highest", + "time": "Time", + "distance": "Distance", + "score": "Points" + } }, "stations": { "header": "Station Statistics", "station": "Station", - "time": "Time" + "time": "Time: {{time}}", + "sortby": { + "title": "Sort: ", + "min": "Lowest", + "max": "Highest" + } }, "errors": { "notfound": { diff --git a/packages/frontend/src/i18n/languages/pl.json b/packages/frontend/src/i18n/languages/pl.json index 93f140d..4f0eeb0 100644 --- a/packages/frontend/src/i18n/languages/pl.json +++ b/packages/frontend/src/i18n/languages/pl.json @@ -63,14 +63,27 @@ "trains": { "header": "Statystyki pociągów", "train": "Pociąg", - "distance": "Dystans", - "points": "Punkty", - "time": "Czas" + "distance": "Dystans: {{distance}}km", + "score": "Punkty: {{score}}", + "time": "Czas: {{time}}", + "sortby": { + "title": "Sortowanie: ", + "min": "Od najniższej", + "max": "Od najwyższej", + "time": "Czas", + "distance": "Dystans", + "score": "Punkty" + } }, "stations": { "header": "Statystyki stacji", "station": "Stacja", - "time": "Czas" + "time": "Czas: {{time}}", + "sortby": { + "title": "Sortowanie: ", + "min": "Od najniższej", + "max": "Od najwyższej" + } }, "errors": { "notfound": { diff --git a/packages/frontend/src/pages/profiles/Profile.tsx b/packages/frontend/src/pages/profiles/Profile.tsx index cfccf1e..f7d83ad 100644 --- a/packages/frontend/src/pages/profiles/Profile.tsx +++ b/packages/frontend/src/pages/profiles/Profile.tsx @@ -27,37 +27,37 @@ import useSWR from "swr"; import { get } from "../../util/fetcher.ts"; -export const Profile = () => -{ +export const Profile = () => { const { id } = useParams(); - const { data, error, isLoading } = useSWR(`/profiles/${ id }`, get, { refreshInterval: 5_000, errorRetryCount: 5 }); + const { data, error, isLoading } = useSWR(`/profiles/${id}`, get, { refreshInterval: 5_000, errorRetryCount: 5 }); + const images = useSWR(`/images/`, get); const { t } = useTranslation(); return ( - <> - {/* LOADING */ } - { isLoading && } - {/* ERROR */ } - { error && } - {/* BLACKLISTED */ } - { data && data.code === 403 && } - { data && data.code === 403 && } - {/* NOT FOUND */ } - { data && data.code === 404 && } - { data && data.code === 404 && } + <> + {/* LOADING */} + {(isLoading || images.isLoading) && } + {/* ERROR */} + {(error || images.error) && } + {/* BLACKLISTED */} + {data && data.code === 403 && } + {data && data.code === 403 && } + {/* NOT FOUND */} + {data && data.code === 404 && } + {data && data.code === 404 && } - {/* SUCCESS */ } - { data && data.code === 200 && } - { data && data.code === 200 && } - + {/* SUCCESS */} + {data && data.code === 200 && images.data && images.data.code === 200 && } + {data && data.code === 200 && images.data && images.data.code === 200 && } + ); }; diff --git a/packages/frontend/src/util/fetcher.ts b/packages/frontend/src/util/fetcher.ts index 64dab12..dd006d3 100644 --- a/packages/frontend/src/util/fetcher.ts +++ b/packages/frontend/src/util/fetcher.ts @@ -14,6 +14,6 @@ * See LICENSE for more. */ -export const get = (url: string) => fetch(`${ import.meta.env.VITE_API_URL }${ url }`, { signal: AbortSignal.timeout(2500) }).then((res) => res.json()); +export const get = (url: string) => fetch(`${ import.meta.env.VITE_API_URL }${ url }`, { signal: AbortSignal.timeout(10000) }).then((res) => res.json()); -export const post = (url: string, body?: any, headers: Record = {}) => fetch(`${ import.meta.env.VITE_API_URL }${ url }`, { signal: AbortSignal.timeout(2500), method: "POST", body: JSON.stringify(body), headers: Object.assign(headers, { "Content-Type": "application/json" }) }).then((res) => res.json()); \ No newline at end of file +export const post = (url: string, body?: any, headers: Record = {}) => fetch(`${ import.meta.env.VITE_API_URL }${ url }`, { signal: AbortSignal.timeout(10000), method: "POST", body: JSON.stringify(body), headers: Object.assign(headers, { "Content-Type": "application/json" }) }).then((res) => res.json()); \ No newline at end of file