v3 release #75

Merged
alekswilc merged 63 commits from v3 into main 2024-12-13 20:29:17 +01:00
31 changed files with 1636 additions and 1536 deletions
Showing only changes of commit 14ab862b7d - Show all commits

View File

@ -1,12 +1,9 @@
export const ErrorAlert = ({ title, description }: { title: string, description: string }) => import { ErrorAlertIcon } from '../icons/AlertIcons.tsx';
<div
export const ErrorAlert = ({ title, description }: { title: string, description: string }) => <div
className="flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9"> className="flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]"> <div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]">
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="<http://www.w3.org/2000/svg>"> <ErrorAlertIcon />
<path
d="M6.4917 7.65579L11.106 12.2645C11.2545 12.4128 11.4715 12.5 11.6738 12.5C11.8762 12.5 12.0931 12.4128 12.2416 12.2645C12.5621 11.9445 12.5623 11.4317 12.2423 11.1114C12.2422 11.1113 12.2422 11.1113 12.2422 11.1113C12.242 11.1111 12.2418 11.1109 12.2416 11.1107L7.64539 6.50351L12.2589 1.91221L12.2595 1.91158C12.5802 1.59132 12.5802 1.07805 12.2595 0.757793C11.9393 0.437994 11.4268 0.437869 11.1064 0.757418C11.1063 0.757543 11.1062 0.757668 11.106 0.757793L6.49234 5.34931L1.89459 0.740581L1.89396 0.739942C1.57364 0.420019 1.0608 0.420019 0.740487 0.739944C0.42005 1.05999 0.419837 1.57279 0.73985 1.89309L6.4917 7.65579ZM6.4917 7.65579L1.89459 12.2639L1.89395 12.2645C1.74546 12.4128 1.52854 12.5 1.32616 12.5C1.12377 12.5 0.906853 12.4128 0.758361 12.2645L1.1117 11.9108L0.758358 12.2645C0.437984 11.9445 0.437708 11.4319 0.757539 11.1116C0.757812 11.1113 0.758086 11.111 0.75836 11.1107L5.33864 6.50287L0.740487 1.89373L6.4917 7.65579Z"
fill="#ffffff" stroke="#ffffff"></path>
</svg>
</div> </div>
<div className="w-full"> <div className="w-full">
<h5 className="mb-3 font-semibold text-[#B45454]"> <h5 className="mb-3 font-semibold text-[#B45454]">

View File

@ -1,12 +1,10 @@
import { SuccessAlertIcon } from '../icons/AlertIcons.tsx';
export const SuccessAlert = ({ title, description }: { title: string, description: string }) => export const SuccessAlert = ({ title, description }: { title: string, description: string }) =>
<div <div
className="flex w-full border-l-6 border-[#34D399] bg-[#34D399] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9"> className="flex w-full border-l-6 border-[#34D399] bg-[#34D399] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#34D399]"> <div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#34D399]">
<svg width="16" height="12" viewBox="0 0 16 12" fill="none" xmlns="<http://www.w3.org/2000/svg>"> <SuccessAlertIcon />
<path
d="M15.2984 0.826822L15.2868 0.811827L15.2741 0.797751C14.9173 0.401867 14.3238 0.400754 13.9657 0.794406L5.91888 9.45376L2.05667 5.2868C1.69856 4.89287 1.10487 4.89389 0.747996 5.28987C0.417335 5.65675 0.417335 6.22337 0.747996 6.59026L0.747959 6.59029L0.752701 6.59541L4.86742 11.0348C5.14445 11.3405 5.52858 11.5 5.89581 11.5C6.29242 11.5 6.65178 11.3355 6.92401 11.035L15.2162 2.11161C15.5833 1.74452 15.576 1.18615 15.2984 0.826822Z"
fill="white" stroke="white"></path>
</svg>
</div> </div>
<div className="w-full"> <div className="w-full">
<h5 className="mb-3 text-lg font-semibold text-black dark:text-[#34D399] "> <h5 className="mb-3 text-lg font-semibold text-black dark:text-[#34D399] ">

View File

@ -1,12 +1,10 @@
import { WarningAlertIcon } from '../icons/AlertIcons.tsx';
export const WarningAlert = ({ title, description }: { title: string, description: string }) => export const WarningAlert = ({ title, description }: { title: string, description: string }) =>
<div <div
className="flex w-full border-l-6 border-warning bg-warning bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9"> className="flex w-full border-l-6 border-warning bg-warning bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-9 items-center justify-center rounded-lg bg-warning bg-opacity-30"> <div className="mr-5 flex h-9 w-9 items-center justify-center rounded-lg bg-warning bg-opacity-30">
<svg width="19" height="16" viewBox="0 0 19 16" fill="none" xmlns="<http://www.w3.org/2000/svg>"> <WarningAlertIcon />
<path
d="M1.50493 16H17.5023C18.6204 16 19.3413 14.9018 18.8354 13.9735L10.8367 0.770573C10.2852 -0.256858 8.70677 -0.256858 8.15528 0.770573L0.156617 13.9735C-0.334072 14.8998 0.386764 16 1.50493 16ZM10.7585 12.9298C10.7585 13.6155 10.2223 14.1433 9.45583 14.1433C8.6894 14.1433 8.15311 13.6155 8.15311 12.9298V12.9015C8.15311 12.2159 8.6894 11.688 9.45583 11.688C10.2223 11.688 10.7585 12.2159 10.7585 12.9015V12.9298ZM8.75236 4.01062H10.2548C10.6674 4.01062 10.9127 4.33826 10.8671 4.75288L10.2071 10.1186C10.1615 10.5049 9.88572 10.7455 9.50142 10.7455C9.11929 10.7455 8.84138 10.5028 8.79579 10.1186L8.13574 4.75288C8.09449 4.33826 8.33984 4.01062 8.75236 4.01062Z"
fill="#FBBF24"></path>
</svg>
</div> </div>
<div className="w-full"> <div className="w-full">
<h5 className="mb-3 text-lg font-semibold text-[#9D5425]"> <h5 className="mb-3 text-lg font-semibold text-[#9D5425]">

View File

@ -1,63 +1,35 @@
import useColorMode from "../../../hooks/useColorMode"; import useColorMode from '../../../hooks/useColorMode';
import { DarkIcon, LightIcon } from '../icons/DarkModeSwitchIcons.tsx';
const DarkModeSwitcher = () => const DarkModeSwitcher = () => {
{
const [colorMode, setColorMode] = useColorMode(); const [colorMode, setColorMode] = useColorMode();
return ( return (
<li> <li>
<label <label
className={`relative m-0 block h-7.5 w-14 rounded-full ${ className={`relative m-0 block h-7.5 w-14 rounded-full ${
colorMode === "dark" ? "bg-primary" : "bg-stroke" colorMode === 'dark' ? 'bg-primary' : 'bg-stroke'
}`} }`}
> >
<input <input
type="checkbox" type="checkbox"
onChange={ () => onChange={() => {
{ if (typeof setColorMode === 'function') {
if (typeof setColorMode === "function") setColorMode(colorMode === 'light' ? 'dark' : 'light');
{
setColorMode(colorMode === "light" ? "dark" : "light");
} }
}} }}
className="dur absolute top-0 z-50 m-0 h-full w-full cursor-pointer opacity-0" className="dur absolute top-0 z-50 m-0 h-full w-full cursor-pointer opacity-0"
/> />
<span <span
className={`absolute top-1/2 left-[3px] flex h-6 w-6 -translate-y-1/2 translate-x-0 items-center justify-center rounded-full bg-white shadow-switcher duration-75 ease-linear ${ className={`absolute top-1/2 left-[3px] flex h-6 w-6 -translate-y-1/2 translate-x-0 items-center justify-center rounded-full bg-white shadow-switcher duration-75 ease-linear ${
colorMode === "dark" && "!right-[3px] !translate-x-full" colorMode === 'dark' && '!right-[3px] !translate-x-full'
}`} }`}
> >
<span className="dark:hidden"> <span className="dark:hidden">
<svg <LightIcon />
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.99992 12.6666C10.5772 12.6666 12.6666 10.5772 12.6666 7.99992C12.6666 5.42259 10.5772 3.33325 7.99992 3.33325C5.42259 3.33325 3.33325 5.42259 3.33325 7.99992C3.33325 10.5772 5.42259 12.6666 7.99992 12.6666Z"
fill="#969AA1"
/>
<path
d="M8.00008 15.3067C7.63341 15.3067 7.33342 15.0334 7.33342 14.6667V14.6134C7.33342 14.2467 7.63341 13.9467 8.00008 13.9467C8.36675 13.9467 8.66675 14.2467 8.66675 14.6134C8.66675 14.9801 8.36675 15.3067 8.00008 15.3067ZM12.7601 13.4267C12.5867 13.4267 12.4201 13.3601 12.2867 13.2334L12.2001 13.1467C11.9401 12.8867 11.9401 12.4667 12.2001 12.2067C12.4601 11.9467 12.8801 11.9467 13.1401 12.2067L13.2267 12.2934C13.4867 12.5534 13.4867 12.9734 13.2267 13.2334C13.1001 13.3601 12.9334 13.4267 12.7601 13.4267ZM3.24008 13.4267C3.06675 13.4267 2.90008 13.3601 2.76675 13.2334C2.50675 12.9734 2.50675 12.5534 2.76675 12.2934L2.85342 12.2067C3.11342 11.9467 3.53341 11.9467 3.79341 12.2067C4.05341 12.4667 4.05341 12.8867 3.79341 13.1467L3.70675 13.2334C3.58008 13.3601 3.40675 13.4267 3.24008 13.4267ZM14.6667 8.66675H14.6134C14.2467 8.66675 13.9467 8.36675 13.9467 8.00008C13.9467 7.63341 14.2467 7.33342 14.6134 7.33342C14.9801 7.33342 15.3067 7.63341 15.3067 8.00008C15.3067 8.36675 15.0334 8.66675 14.6667 8.66675ZM1.38675 8.66675H1.33341C0.966748 8.66675 0.666748 8.36675 0.666748 8.00008C0.666748 7.63341 0.966748 7.33342 1.33341 7.33342C1.70008 7.33342 2.02675 7.63341 2.02675 8.00008C2.02675 8.36675 1.75341 8.66675 1.38675 8.66675ZM12.6734 3.99341C12.5001 3.99341 12.3334 3.92675 12.2001 3.80008C11.9401 3.54008 11.9401 3.12008 12.2001 2.86008L12.2867 2.77341C12.5467 2.51341 12.9667 2.51341 13.2267 2.77341C13.4867 3.03341 13.4867 3.45341 13.2267 3.71341L13.1401 3.80008C13.0134 3.92675 12.8467 3.99341 12.6734 3.99341ZM3.32675 3.99341C3.15341 3.99341 2.98675 3.92675 2.85342 3.80008L2.76675 3.70675C2.50675 3.44675 2.50675 3.02675 2.76675 2.76675C3.02675 2.50675 3.44675 2.50675 3.70675 2.76675L3.79341 2.85342C4.05341 3.11342 4.05341 3.53341 3.79341 3.79341C3.66675 3.92675 3.49341 3.99341 3.32675 3.99341ZM8.00008 2.02675C7.63341 2.02675 7.33342 1.75341 7.33342 1.38675V1.33341C7.33342 0.966748 7.63341 0.666748 8.00008 0.666748C8.36675 0.666748 8.66675 0.966748 8.66675 1.33341C8.66675 1.70008 8.36675 2.02675 8.00008 2.02675Z"
fill="#969AA1"
/>
</svg>
</span> </span>
<span className="hidden dark:inline-block"> <span className="hidden dark:inline-block">
<svg <DarkIcon />
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.3533 10.62C14.2466 10.44 13.9466 10.16 13.1999 10.2933C12.7866 10.3667 12.3666 10.4 11.9466 10.38C10.3933 10.3133 8.98659 9.6 8.00659 8.5C7.13993 7.53333 6.60659 6.27333 6.59993 4.91333C6.59993 4.15333 6.74659 3.42 7.04659 2.72666C7.33993 2.05333 7.13326 1.7 6.98659 1.55333C6.83326 1.4 6.47326 1.18666 5.76659 1.48C3.03993 2.62666 1.35326 5.36 1.55326 8.28666C1.75326 11.04 3.68659 13.3933 6.24659 14.28C6.85993 14.4933 7.50659 14.62 8.17326 14.6467C8.27993 14.6533 8.38659 14.66 8.49326 14.66C10.7266 14.66 12.8199 13.6067 14.1399 11.8133C14.5866 11.1933 14.4666 10.8 14.3533 10.62Z"
fill="#969AA1"
/>
</svg>
</span> </span>
</span> </span>
</label> </label>

View File

@ -0,0 +1,74 @@
import DarkModeSwitcher from './DarkModeSwitcher.tsx';
import ReactCountryFlag from 'react-country-flag';
import i18n from 'i18next';
export const Header = (props: {
sidebarOpen: string | boolean | undefined;
setSidebarOpen: (arg0: boolean) => void;
}) => {
return (
<header className="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div className="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<div className="flex items-center gap-2 sm:gap-4 lg:hidden">
<button
aria-controls="sidebar"
onClick={(e) => {
e.stopPropagation();
props.setSidebarOpen(!props.sidebarOpen);
}}
className="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
>
<span className="relative block h-5.5 w-5.5 cursor-pointer">
<span className="du-block absolute right-0 h-full w-full">
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-[0] duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!w-full delay-300'
}`}
></span>
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-150 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && 'delay-400 !w-full'
}`}
></span>
<span
className={`relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-200 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!w-full delay-500'
}`}
></span>
</span>
<span className="absolute right-0 h-full w-full rotate-45">
<span
className={`absolute left-2.5 top-0 block h-full w-0.5 rounded-sm bg-black delay-300 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!h-0 !delay-[0]'
}`}
></span>
<span
className={`delay-400 absolute left-0 top-2.5 block h-0.5 w-full rounded-sm bg-black duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!h-0 !delay-200'
}`}
></span>
</span>
</span>
</button>
</div>
<div className="hidden sm:block"></div>
<div className="flex items-center gap-3 2xsm:gap-7">
<ul className="flex items-center gap-2 2xsm:gap-4">
<a className="cursor-pointer" onClick={() => i18n.changeLanguage('pl')}>
<ReactCountryFlag countryCode={'PL'} svg />
</a>
<a className="cursor-pointer" onClick={() => i18n.changeLanguage('en')}>
<ReactCountryFlag countryCode={'US'} svg />
</a>
</ul>
<ul className="flex items-center gap-2 2xsm:gap-4">
<DarkModeSwitcher />
</ul>
</div>
</div>
</header>
);
};

View File

@ -1,74 +0,0 @@
import DarkModeSwitcher from "./DarkModeSwitcher.tsx";
const Header = (props: {
sidebarOpen: string | boolean | undefined;
setSidebarOpen: (arg0: boolean) => void;
}) =>
{
return (
<header className="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div className="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11">
<div className="flex items-center gap-2 sm:gap-4 lg:hidden">
{/* <!-- Hamburger Toggle BTN --> */ }
<button
aria-controls="sidebar"
onClick={ (e) =>
{
e.stopPropagation();
props.setSidebarOpen(!props.sidebarOpen);
} }
className="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
>
<span className="relative block h-5.5 w-5.5 cursor-pointer">
<span className="du-block absolute right-0 h-full w-full">
<span
className={ `relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-[0] duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!w-full delay-300"
}` }
></span>
<span
className={ `relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-150 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "delay-400 !w-full"
}` }
></span>
<span
className={ `relative left-0 top-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-200 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!w-full delay-500"
}` }
></span>
</span>
<span className="absolute right-0 h-full w-full rotate-45">
<span
className={ `absolute left-2.5 top-0 block h-full w-0.5 rounded-sm bg-black delay-300 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!h-0 !delay-[0]"
}` }
></span>
<span
className={ `delay-400 absolute left-0 top-2.5 block h-0.5 w-full rounded-sm bg-black duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && "!h-0 !delay-200"
}` }
></span>
</span>
</span>
</button>
{/* <!-- Hamburger Toggle BTN --> */ }
</div>
<div className="hidden sm:block"></div>
<div className="flex items-center gap-3 2xsm:gap-7">
<ul className="flex items-center gap-2 2xsm:gap-4">
{/* <!-- Dark Mode Toggler --> */ }
<DarkModeSwitcher/>
{/* <!-- Dark Mode Toggler --> */ }
{/* LanguageSwitcher */ }
</ul>
</div>
</div>
</header>
);
};
export default Header;

View File

@ -0,0 +1,20 @@
export const ErrorAlertIcon = () => <svg width="13" height="13" viewBox="0 0 13 13" fill="none"
xmlns="<http://www.w3.org/2000/svg>">
<path
d="M6.4917 7.65579L11.106 12.2645C11.2545 12.4128 11.4715 12.5 11.6738 12.5C11.8762 12.5 12.0931 12.4128 12.2416 12.2645C12.5621 11.9445 12.5623 11.4317 12.2423 11.1114C12.2422 11.1113 12.2422 11.1113 12.2422 11.1113C12.242 11.1111 12.2418 11.1109 12.2416 11.1107L7.64539 6.50351L12.2589 1.91221L12.2595 1.91158C12.5802 1.59132 12.5802 1.07805 12.2595 0.757793C11.9393 0.437994 11.4268 0.437869 11.1064 0.757418C11.1063 0.757543 11.1062 0.757668 11.106 0.757793L6.49234 5.34931L1.89459 0.740581L1.89396 0.739942C1.57364 0.420019 1.0608 0.420019 0.740487 0.739944C0.42005 1.05999 0.419837 1.57279 0.73985 1.89309L6.4917 7.65579ZM6.4917 7.65579L1.89459 12.2639L1.89395 12.2645C1.74546 12.4128 1.52854 12.5 1.32616 12.5C1.12377 12.5 0.906853 12.4128 0.758361 12.2645L1.1117 11.9108L0.758358 12.2645C0.437984 11.9445 0.437708 11.4319 0.757539 11.1116C0.757812 11.1113 0.758086 11.111 0.75836 11.1107L5.33864 6.50287L0.740487 1.89373L6.4917 7.65579Z"
fill="#ffffff" stroke="#ffffff"></path>
</svg>;
export const SuccessAlertIcon = () => <svg width="16" height="12" viewBox="0 0 16 12" fill="none"
xmlns="<http://www.w3.org/2000/svg>">
<path
d="M15.2984 0.826822L15.2868 0.811827L15.2741 0.797751C14.9173 0.401867 14.3238 0.400754 13.9657 0.794406L5.91888 9.45376L2.05667 5.2868C1.69856 4.89287 1.10487 4.89389 0.747996 5.28987C0.417335 5.65675 0.417335 6.22337 0.747996 6.59026L0.747959 6.59029L0.752701 6.59541L4.86742 11.0348C5.14445 11.3405 5.52858 11.5 5.89581 11.5C6.29242 11.5 6.65178 11.3355 6.92401 11.035L15.2162 2.11161C15.5833 1.74452 15.576 1.18615 15.2984 0.826822Z"
fill="white" stroke="white"></path>
</svg>;
export const WarningAlertIcon = () => <svg width="19" height="16" viewBox="0 0 19 16" fill="none"
xmlns="<http://www.w3.org/2000/svg>">
<path
d="M1.50493 16H17.5023C18.6204 16 19.3413 14.9018 18.8354 13.9735L10.8367 0.770573C10.2852 -0.256858 8.70677 -0.256858 8.15528 0.770573L0.156617 13.9735C-0.334072 14.8998 0.386764 16 1.50493 16ZM10.7585 12.9298C10.7585 13.6155 10.2223 14.1433 9.45583 14.1433C8.6894 14.1433 8.15311 13.6155 8.15311 12.9298V12.9015C8.15311 12.2159 8.6894 11.688 9.45583 11.688C10.2223 11.688 10.7585 12.2159 10.7585 12.9015V12.9298ZM8.75236 4.01062H10.2548C10.6674 4.01062 10.9127 4.33826 10.8671 4.75288L10.2071 10.1186C10.1615 10.5049 9.88572 10.7455 9.50142 10.7455C9.11929 10.7455 8.84138 10.5028 8.79579 10.1186L8.13574 4.75288C8.09449 4.33826 8.33984 4.01062 8.75236 4.01062Z"
fill="#FBBF24"></path>
</svg>;

View File

@ -0,0 +1,18 @@
export const ArrowIcon = ({ rotated }: { rotated?: boolean }) =>
<svg
className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${
rotated && 'rotate-180'
}`}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"
fill=""
/>
</svg>;

View File

@ -0,0 +1,29 @@
export const LightIcon = () => <svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.99992 12.6666C10.5772 12.6666 12.6666 10.5772 12.6666 7.99992C12.6666 5.42259 10.5772 3.33325 7.99992 3.33325C5.42259 3.33325 3.33325 5.42259 3.33325 7.99992C3.33325 10.5772 5.42259 12.6666 7.99992 12.6666Z"
fill="#969AA1"
/>
<path
d="M8.00008 15.3067C7.63341 15.3067 7.33342 15.0334 7.33342 14.6667V14.6134C7.33342 14.2467 7.63341 13.9467 8.00008 13.9467C8.36675 13.9467 8.66675 14.2467 8.66675 14.6134C8.66675 14.9801 8.36675 15.3067 8.00008 15.3067ZM12.7601 13.4267C12.5867 13.4267 12.4201 13.3601 12.2867 13.2334L12.2001 13.1467C11.9401 12.8867 11.9401 12.4667 12.2001 12.2067C12.4601 11.9467 12.8801 11.9467 13.1401 12.2067L13.2267 12.2934C13.4867 12.5534 13.4867 12.9734 13.2267 13.2334C13.1001 13.3601 12.9334 13.4267 12.7601 13.4267ZM3.24008 13.4267C3.06675 13.4267 2.90008 13.3601 2.76675 13.2334C2.50675 12.9734 2.50675 12.5534 2.76675 12.2934L2.85342 12.2067C3.11342 11.9467 3.53341 11.9467 3.79341 12.2067C4.05341 12.4667 4.05341 12.8867 3.79341 13.1467L3.70675 13.2334C3.58008 13.3601 3.40675 13.4267 3.24008 13.4267ZM14.6667 8.66675H14.6134C14.2467 8.66675 13.9467 8.36675 13.9467 8.00008C13.9467 7.63341 14.2467 7.33342 14.6134 7.33342C14.9801 7.33342 15.3067 7.63341 15.3067 8.00008C15.3067 8.36675 15.0334 8.66675 14.6667 8.66675ZM1.38675 8.66675H1.33341C0.966748 8.66675 0.666748 8.36675 0.666748 8.00008C0.666748 7.63341 0.966748 7.33342 1.33341 7.33342C1.70008 7.33342 2.02675 7.63341 2.02675 8.00008C2.02675 8.36675 1.75341 8.66675 1.38675 8.66675ZM12.6734 3.99341C12.5001 3.99341 12.3334 3.92675 12.2001 3.80008C11.9401 3.54008 11.9401 3.12008 12.2001 2.86008L12.2867 2.77341C12.5467 2.51341 12.9667 2.51341 13.2267 2.77341C13.4867 3.03341 13.4867 3.45341 13.2267 3.71341L13.1401 3.80008C13.0134 3.92675 12.8467 3.99341 12.6734 3.99341ZM3.32675 3.99341C3.15341 3.99341 2.98675 3.92675 2.85342 3.80008L2.76675 3.70675C2.50675 3.44675 2.50675 3.02675 2.76675 2.76675C3.02675 2.50675 3.44675 2.50675 3.70675 2.76675L3.79341 2.85342C4.05341 3.11342 4.05341 3.53341 3.79341 3.79341C3.66675 3.92675 3.49341 3.99341 3.32675 3.99341ZM8.00008 2.02675C7.63341 2.02675 7.33342 1.75341 7.33342 1.38675V1.33341C7.33342 0.966748 7.63341 0.666748 8.00008 0.666748C8.36675 0.666748 8.66675 0.966748 8.66675 1.33341C8.66675 1.70008 8.36675 2.02675 8.00008 2.02675Z"
fill="#969AA1"
/>
</svg>;
export const DarkIcon = () => <svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14.3533 10.62C14.2466 10.44 13.9466 10.16 13.1999 10.2933C12.7866 10.3667 12.3666 10.4 11.9466 10.38C10.3933 10.3133 8.98659 9.6 8.00659 8.5C7.13993 7.53333 6.60659 6.27333 6.59993 4.91333C6.59993 4.15333 6.74659 3.42 7.04659 2.72666C7.33993 2.05333 7.13326 1.7 6.98659 1.55333C6.83326 1.4 6.47326 1.18666 5.76659 1.48C3.03993 2.62666 1.35326 5.36 1.55326 8.28666C1.75326 11.04 3.68659 13.3933 6.24659 14.28C6.85993 14.4933 7.50659 14.62 8.17326 14.6467C8.27993 14.6533 8.38659 14.66 8.49326 14.66C10.7266 14.66 12.8199 13.6067 14.1399 11.8133C14.5866 11.1933 14.4666 10.8 14.3533 10.62Z"
fill="#969AA1"
/>
</svg>;

View File

@ -0,0 +1,13 @@
export const HamburgerGoBackIcon = () => <svg
className="fill-current"
width="20"
height="18"
viewBox="0 0 20 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 8.175H2.98748L9.36248 1.6875C9.69998 1.35 9.69998 0.825 9.36248 0.4875C9.02498 0.15 8.49998 0.15 8.16248 0.4875L0.399976 8.3625C0.0624756 8.7 0.0624756 9.225 0.399976 9.5625L8.16248 17.4375C8.31248 17.5875 8.53748 17.7 8.76248 17.7C8.98748 17.7 9.17498 17.625 9.36248 17.475C9.69998 17.1375 9.69998 16.6125 9.36248 16.275L3.02498 9.8625H19C19.45 9.8625 19.825 9.4875 19.825 9.0375C19.825 8.55 19.45 8.175 19 8.175Z"
fill=""
/>
</svg>;

View File

@ -1,28 +1,24 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import { ErrorAlertIcon } from '../icons/AlertIcons.tsx';
export const LoadError = () => export const LoadError = () => {
{
const { t } = useTranslation(); const { t } = useTranslation();
return <div return <div
className="flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9"> className="flex w-full border-l-6 border-[#F87171] bg-[#F87171] bg-opacity-[15%] dark:bg-[#1B1B24] px-7 py-8 shadow-md dark:bg-opacity-30 md:p-9">
<div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]"> <div className="mr-5 flex h-9 w-full max-w-[36px] items-center justify-center rounded-lg bg-[#F87171]">
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="<http://www.w3.org/2000/svg>"> <ErrorAlertIcon />
<path
d="M6.4917 7.65579L11.106 12.2645C11.2545 12.4128 11.4715 12.5 11.6738 12.5C11.8762 12.5 12.0931 12.4128 12.2416 12.2645C12.5621 11.9445 12.5623 11.4317 12.2423 11.1114C12.2422 11.1113 12.2422 11.1113 12.2422 11.1113C12.242 11.1111 12.2418 11.1109 12.2416 11.1107L7.64539 6.50351L12.2589 1.91221L12.2595 1.91158C12.5802 1.59132 12.5802 1.07805 12.2595 0.757793C11.9393 0.437994 11.4268 0.437869 11.1064 0.757418C11.1063 0.757543 11.1062 0.757668 11.106 0.757793L6.49234 5.34931L1.89459 0.740581L1.89396 0.739942C1.57364 0.420019 1.0608 0.420019 0.740487 0.739944C0.42005 1.05999 0.419837 1.57279 0.73985 1.89309L6.4917 7.65579ZM6.4917 7.65579L1.89459 12.2639L1.89395 12.2645C1.74546 12.4128 1.52854 12.5 1.32616 12.5C1.12377 12.5 0.906853 12.4128 0.758361 12.2645L1.1117 11.9108L0.758358 12.2645C0.437984 11.9445 0.437708 11.4319 0.757539 11.1116C0.757812 11.1113 0.758086 11.111 0.75836 11.1107L5.33864 6.50287L0.740487 1.89373L6.4917 7.65579Z"
fill="#ffffff" stroke="#ffffff"></path>
</svg>
</div> </div>
<div className="w-full"> <div className="w-full">
<h5 className="mb-3 font-semibold text-[#B45454]"> <h5 className="mb-3 font-semibold text-[#B45454]">
{ t("content_loader.error.header") } {t('content_loader.error.header')}
</h5> </h5>
<ul> <ul>
<li className="leading-relaxed text-[#CD5D5D]"> <li className="leading-relaxed text-[#CD5D5D]">
{ t("content_loader.error.description") } {t('content_loader.error.description')}
</li> </li>
<li className="leading-relaxed text-[#CD5D5D]"> <li className="leading-relaxed text-[#CD5D5D]">
<div className="pt-4"> <div className="pt-4">
@ -30,12 +26,12 @@ export const LoadError = () =>
<div className="mb-7.5 flex flex-wrap gap-4"> <div className="mb-7.5 flex flex-wrap gap-4">
<Link <Link
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-70 lg:px-6 xl:px-8" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-70 lg:px-6 xl:px-8"
to="https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/issues/new">{ t("content_loader.error.report") }</Link> to="https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/issues/new">{t('content_loader.error.report')}</Link>
<Link <Link
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-70 lg:px-6 xl:px-8" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-70 lg:px-6 xl:px-8"
to="#" to="#"
onClick={ () => window.location.reload() }>{ t("content_loader.error.refresh") }</Link> onClick={() => window.location.reload()}>{t('content_loader.error.refresh')}</Link>
</div> </div>
</div> </div>
@ -46,25 +42,19 @@ export const LoadError = () =>
</div>; </div>;
}; };
export const ContentLoader = () => export const ContentLoader = () => {
{
const [error, setError] = useState(false); const [error, setError] = useState(false);
useEffect(() => useEffect(() => {
{ new Promise(res => setTimeout(res, 5000)).then(() => {
new Promise(res => setTimeout(res, 5000)).then(() =>
{
setError(true); setError(true);
}); });
}, []); }, []);
return ( return (
<> <>
{error ? <LoadError /> : <div {error ? <LoadError /> : <div
className="flex h-screen items-center justify-center shadow-default bg-white dark:border-strokedark dark:bg-boxdark"> className="flex h-screen items-center justify-center shadow-default bg-white dark:border-strokedark dark:bg-boxdark">
<div <div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent" />
className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
</div>} </div>}
</> </>
); );

View File

@ -1,9 +1,7 @@
export const Loader = () => export const Loader = () => {
{
return ( return (
<div className="flex h-screen items-center justify-center bg-black"> <div className="flex h-screen items-center justify-center bg-black">
<div <div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent" />
className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
</div> </div>
); );
}; };

View File

@ -0,0 +1,517 @@
import React, { useEffect, useRef, useState } from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import SidebarLinkGroup from './SidebarLinkGroup.tsx';
import { useTranslation } from 'react-i18next';
import { HamburgerGoBackIcon } from '../icons/SidebarIcons.tsx';
import { ArrowIcon } from '../icons/ArrowIcon.tsx';
import { FaHome, FaClipboardList } from 'react-icons/fa';
import { FaChartSimple, FaTrain, FaBuildingFlag } from 'react-icons/fa6';
interface SidebarProps {
sidebarOpen: boolean;
setSidebarOpen: (arg: boolean) => void;
}
export const Sidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => {
const location = useLocation();
const { pathname } = location;
const trigger = useRef<any>(null);
const sidebar = useRef<any>(null);
const storedSidebarExpanded = localStorage.getItem('sidebar-expanded');
const [sidebarExpanded, setSidebarExpanded] = useState(
storedSidebarExpanded === null ? false : storedSidebarExpanded === 'true'
);
const { t } = useTranslation();
// close on click outside
useEffect(() => {
const clickHandler = ({ target }: MouseEvent) => {
if (!sidebar.current || !trigger.current) {
return;
}
if (
!sidebarOpen ||
sidebar.current.contains(target) ||
trigger.current.contains(target)
) {
return;
}
setSidebarOpen(false);
};
document.addEventListener('click', clickHandler);
return () => document.removeEventListener('click', clickHandler);
});
// close if the esc key is pressed
useEffect(() => {
const keyHandler = ({ keyCode }: KeyboardEvent) => {
if (!sidebarOpen || keyCode !== 27) {
return;
}
setSidebarOpen(false);
};
document.addEventListener('keydown', keyHandler);
return () => document.removeEventListener('keydown', keyHandler);
});
useEffect(() => {
localStorage.setItem('sidebar-expanded', sidebarExpanded.toString());
if (sidebarExpanded) {
document.querySelector('body')?.classList.add('sidebar-expanded');
} else {
document.querySelector('body')?.classList.remove('sidebar-expanded');
}
}, [sidebarExpanded]);
return (
<aside
ref={sidebar}
className={`absolute left-0 top-0 z-9999 flex h-screen w-72.5 flex-col overflow-y-hidden bg-black duration-300 ease-linear dark:bg-boxdark lg:static lg:translate-x-0 ${
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
}`}
>
{/* <!-- SIDEBAR HEADER --> */}
<div className="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
<button
ref={trigger}
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-controls="sidebar"
aria-expanded={sidebarOpen}
className="block lg:hidden"
>
<HamburgerGoBackIcon />
</button>
</div>
{/* <!-- SIDEBAR HEADER --> */}
<div className="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
<nav className="mt-5 py-4 px-4 lg:mt-9 lg:px-6">
<div>
<ul className="mb-6 flex flex-col gap-1.5">
<li>
<NavLink
to="/"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
pathname === '/' &&
'bg-graydark dark:bg-meta-4'
}`}
>
<FaHome />
{t('sidebar.home')}
</NavLink>
</li>
</ul>
<ul className="mb-6 flex flex-col gap-1.5">
<h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">
{t('sidebar.info')}
</h3>
<SidebarLinkGroup
activeCondition={
pathname === '/logs' || pathname.includes('logs')
}
>
{(handleClick, open) => {
return (
<React.Fragment>
<NavLink
to="#"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
(pathname === '/logs' ||
pathname.includes('logs')) &&
'bg-graydark dark:bg-meta-4'
}`}
onClick={(e) => {
e.preventDefault();
sidebarExpanded
? handleClick()
: setSidebarExpanded(true);
}}
>
<FaClipboardList />
{t('sidebar.logs')}
<ArrowIcon rotated={open} />
</NavLink>
<div
className={`translate transform overflow-hidden ${
!open && 'hidden'
}`}
>
<ul className="mt-4 mb-5.5 flex flex-col gap-2.5 pl-6">
<li>
<NavLink
to="/logs/stations"
className={({ isActive }) =>
'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +
(isActive && '!text-white')
}
>
<FaBuildingFlag />
{t('sidebar.stations')}
</NavLink>
</li>
<li>
<NavLink
to="/logs/trains"
className={({ isActive }) =>
'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +
(isActive && '!text-white')
}
>
<FaTrain />
{t('sidebar.trains')}
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
<SidebarLinkGroup
activeCondition={
pathname === '/leaderboard' || pathname.includes('leaderboard')
}
>
{(handleClick, open) => {
return (
<React.Fragment>
<NavLink
to="#"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
(pathname === '/leaderboard' ||
pathname.includes('leaderboard')) &&
'bg-graydark dark:bg-meta-4'
}`}
onClick={(e) => {
e.preventDefault();
sidebarExpanded
? handleClick()
: setSidebarExpanded(true);
}}
>
<FaChartSimple />
{t('sidebar.leaderboard')}
<ArrowIcon rotated={open} />
</NavLink>
<div
className={`translate transform overflow-hidden ${
!open && 'hidden'
}`}
>
<ul className="mt-4 mb-5.5 flex flex-col gap-2.5 pl-6">
<li>
<NavLink
to="/leaderboard/stations"
className={({ isActive }) =>
'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +
(isActive && '!text-white')
}
>
<FaBuildingFlag />
{t('sidebar.stations')}
</NavLink>
</li>
<li>
<NavLink
to="/leaderboard/trains"
className={({ isActive }) =>
'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +
(isActive && '!text-white')
}
>
<FaTrain />
{t('sidebar.trains')}
</NavLink>
</li>
</ul>
</div>
</React.Fragment>
);
}}
</SidebarLinkGroup>
</ul>
</div>
{/* TODO: add admin panel with simple auth */}
{/*{false && <div>*/}
{/* <h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">*/}
{/* {t('sidebar.admin')}*/}
{/* </h3>*/}
{/* <ul className="mb-6 flex flex-col gap-1.5">*/}
{/* */}{/*{/* <!-- Menu Item Chart --> */}
{/* <li>*/}
{/* <NavLink*/}
{/* to="/chart"*/}
{/* className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${*/}
{/* pathname.includes('chart') && 'bg-graydark dark:bg-meta-4'*/}
{/* }`}*/}
{/* >*/}
{/* <svg*/}
{/* className="fill-current"*/}
{/* width="18"*/}
{/* height="19"*/}
{/* viewBox="0 0 18 19"*/}
{/* fill="none"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <g clipPath="url(#clip0_130_9801)">*/}
{/* <path*/}
{/* d="M10.8563 0.55835C10.5188 0.55835 10.2095 0.8396 10.2095 1.20522V6.83022C10.2095 7.16773 10.4907 7.4771 10.8563 7.4771H16.8751C17.0438 7.4771 17.2126 7.39272 17.3251 7.28022C17.4376 7.1396 17.4938 6.97085 17.4938 6.8021C17.2688 3.28647 14.3438 0.55835 10.8563 0.55835ZM11.4751 6.15522V1.8521C13.8095 2.13335 15.6938 3.8771 16.1438 6.18335H11.4751V6.15522Z"*/}
{/* fill=""*/}
{/* />*/}
{/* <path*/}
{/* d="M15.3845 8.7427H9.1126V2.69582C9.1126 2.35832 8.83135 2.07707 8.49385 2.07707C8.40947 2.07707 8.3251 2.07707 8.24072 2.07707C3.96572 2.04895 0.506348 5.53645 0.506348 9.81145C0.506348 14.0864 3.99385 17.5739 8.26885 17.5739C12.5438 17.5739 16.0313 14.0864 16.0313 9.81145C16.0313 9.6427 16.0313 9.47395 16.0032 9.33332C16.0032 8.99582 15.722 8.7427 15.3845 8.7427ZM8.26885 16.3083C4.66885 16.3083 1.77197 13.4114 1.77197 9.81145C1.77197 6.3802 4.47197 3.53957 7.8751 3.3427V9.36145C7.8751 9.69895 8.15635 10.0083 8.52197 10.0083H14.7938C14.6813 13.4958 11.7845 16.3083 8.26885 16.3083Z"*/}
{/* fill=""*/}
{/* />*/}
{/* </g>*/}
{/* <defs>*/}
{/* <clipPath id="clip0_130_9801">*/}
{/* <rect*/}
{/* width="18"*/}
{/* height="18"*/}
{/* fill="white"*/}
{/* transform="translate(0 0.052124)"*/}
{/* />*/}
{/* </clipPath>*/}
{/* </defs>*/}
{/* </svg>*/}
{/* Chart*/}
{/* </NavLink>*/}
{/* </li>*/}
{/* */}{/*{/* <!-- Menu Item Chart --> */}
{/* */}{/*{/* <!-- Menu Item Ui Elements --> */}
{/* <SidebarLinkGroup*/}
{/* activeCondition={pathname === '/ui' || pathname.includes('ui')}*/}
{/* >*/}
{/* {(handleClick, open) => {*/}
{/* return (*/}
{/* <React.Fragment>*/}
{/* <NavLink*/}
{/* to="#"*/}
{/* className={`group relative flex items-center gap-2.5 rounded-sm px-4 py-2 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${*/}
{/* (pathname === '/ui' || pathname.includes('ui')) &&*/}
{/* 'bg-graydark dark:bg-meta-4'*/}
{/* }`}*/}
{/* onClick={(e) => {*/}
{/* e.preventDefault();*/}
{/* sidebarExpanded*/}
{/* ? handleClick()*/}
{/* : setSidebarExpanded(true);*/}
{/* }}*/}
{/* >*/}
{/* <svg*/}
{/* className="fill-current"*/}
{/* width="18"*/}
{/* height="19"*/}
{/* viewBox="0 0 18 19"*/}
{/* fill="none"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <g clipPath="url(#clip0_130_9807)">*/}
{/* <path*/}
{/* d="M15.7501 0.55835H2.2501C1.29385 0.55835 0.506348 1.34585 0.506348 2.3021V7.53335C0.506348 8.4896 1.29385 9.2771 2.2501 9.2771H15.7501C16.7063 9.2771 17.4938 8.4896 17.4938 7.53335V2.3021C17.4938 1.34585 16.7063 0.55835 15.7501 0.55835ZM16.2563 7.53335C16.2563 7.8146 16.0313 8.0396 15.7501 8.0396H2.2501C1.96885 8.0396 1.74385 7.8146 1.74385 7.53335V2.3021C1.74385 2.02085 1.96885 1.79585 2.2501 1.79585H15.7501C16.0313 1.79585 16.2563 2.02085 16.2563 2.3021V7.53335Z"*/}
{/* fill=""*/}
{/* />*/}
{/* <path*/}
{/* d="M6.13135 10.9646H2.2501C1.29385 10.9646 0.506348 11.7521 0.506348 12.7083V15.8021C0.506348 16.7583 1.29385 17.5458 2.2501 17.5458H6.13135C7.0876 17.5458 7.8751 16.7583 7.8751 15.8021V12.7083C7.90322 11.7521 7.11572 10.9646 6.13135 10.9646ZM6.6376 15.8021C6.6376 16.0833 6.4126 16.3083 6.13135 16.3083H2.2501C1.96885 16.3083 1.74385 16.0833 1.74385 15.8021V12.7083C1.74385 12.4271 1.96885 12.2021 2.2501 12.2021H6.13135C6.4126 12.2021 6.6376 12.4271 6.6376 12.7083V15.8021Z"*/}
{/* fill=""*/}
{/* />*/}
{/* <path*/}
{/* d="M15.75 10.9646H11.8688C10.9125 10.9646 10.125 11.7521 10.125 12.7083V15.8021C10.125 16.7583 10.9125 17.5458 11.8688 17.5458H15.75C16.7063 17.5458 17.4938 16.7583 17.4938 15.8021V12.7083C17.4938 11.7521 16.7063 10.9646 15.75 10.9646ZM16.2562 15.8021C16.2562 16.0833 16.0312 16.3083 15.75 16.3083H11.8688C11.5875 16.3083 11.3625 16.0833 11.3625 15.8021V12.7083C11.3625 12.4271 11.5875 12.2021 11.8688 12.2021H15.75C16.0312 12.2021 16.2562 12.4271 16.2562 12.7083V15.8021Z"*/}
{/* fill=""*/}
{/* />*/}
{/* </g>*/}
{/* <defs>*/}
{/* <clipPath id="clip0_130_9807">*/}
{/* <rect*/}
{/* width="18"*/}
{/* height="18"*/}
{/* fill="white"*/}
{/* transform="translate(0 0.052124)"*/}
{/* />*/}
{/* </clipPath>*/}
{/* </defs>*/}
{/* </svg>*/}
{/* UI Elements*/}
{/* <svg*/}
{/* className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${*/}
{/* open && 'rotate-180'*/}
{/* }`}*/}
{/* width="20"*/}
{/* height="20"*/}
{/* viewBox="0 0 20 20"*/}
{/* fill="none"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <path*/}
{/* fillRule="evenodd"*/}
{/* clipRule="evenodd"*/}
{/* d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"*/}
{/* fill=""*/}
{/* />*/}
{/* </svg>*/}
{/* </NavLink>*/}
{/* */}{/*{/* <!-- Dropdown Menu Start --> */}
{/* <div*/}
{/* className={`translate transform overflow-hidden ${*/}
{/* !open && 'hidden'*/}
{/* }`}*/}
{/* >*/}
{/* <ul className="mb-5.5 mt-4 flex flex-col gap-2.5 pl-6">*/}
{/* <li>*/}
{/* <NavLink*/}
{/* to="/ui/alerts"*/}
{/* className={({ isActive }) =>*/}
{/* 'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +*/}
{/* (isActive && '!text-white')*/}
{/* }*/}
{/* >*/}
{/* Alerts*/}
{/* </NavLink>*/}
{/* </li>*/}
{/* <li>*/}
{/* <NavLink*/}
{/* to="/ui/buttons"*/}
{/* className={({ isActive }) =>*/}
{/* 'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +*/}
{/* (isActive && '!text-white')*/}
{/* }*/}
{/* >*/}
{/* Buttons*/}
{/* </NavLink>*/}
{/* </li>*/}
{/* </ul>*/}
{/* </div>*/}
{/* */}{/*{/* <!-- Dropdown Menu End --> */}
{/* </React.Fragment>*/}
{/* );*/}
{/* }}*/}
{/* </SidebarLinkGroup>*/}
{/* */}{/*{/* <!-- Menu Item Ui Elements --> */}
{/* */}{/*{/* <!-- Menu Item Auth Pages --> */}
{/* <SidebarLinkGroup*/}
{/* activeCondition={*/}
{/* pathname === '/auth' || pathname.includes('auth')*/}
{/* }*/}
{/* >*/}
{/* {(handleClick, open) => {*/}
{/* return (*/}
{/* <React.Fragment>*/}
{/* <NavLink*/}
{/* to="#"*/}
{/* className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${*/}
{/* (pathname === '/auth' || pathname.includes('auth')) &&*/}
{/* 'bg-graydark dark:bg-meta-4'*/}
{/* }`}*/}
{/* onClick={(e) => {*/}
{/* e.preventDefault();*/}
{/* sidebarExpanded*/}
{/* ? handleClick()*/}
{/* : setSidebarExpanded(true);*/}
{/* }}*/}
{/* >*/}
{/* <svg*/}
{/* className="fill-current"*/}
{/* width="18"*/}
{/* height="19"*/}
{/* viewBox="0 0 18 19"*/}
{/* fill="none"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <g clipPath="url(#clip0_130_9814)">*/}
{/* <path*/}
{/* d="M12.7127 0.55835H9.53457C8.80332 0.55835 8.18457 1.1771 8.18457 1.90835V3.84897C8.18457 4.18647 8.46582 4.46772 8.80332 4.46772C9.14082 4.46772 9.45019 4.18647 9.45019 3.84897V1.88022C9.45019 1.82397 9.47832 1.79585 9.53457 1.79585H12.7127C13.3877 1.79585 13.9221 2.33022 13.9221 3.00522V15.0709C13.9221 15.7459 13.3877 16.2802 12.7127 16.2802H9.53457C9.47832 16.2802 9.45019 16.2521 9.45019 16.1959V14.2552C9.45019 13.9177 9.16894 13.6365 8.80332 13.6365C8.43769 13.6365 8.18457 13.9177 8.18457 14.2552V16.1959C8.18457 16.9271 8.80332 17.5459 9.53457 17.5459H12.7127C14.0908 17.5459 15.1877 16.4209 15.1877 15.0709V3.03335C15.1877 1.65522 14.0627 0.55835 12.7127 0.55835Z"*/}
{/* fill=""*/}
{/* />*/}
{/* <path*/}
{/* d="M10.4346 8.60205L7.62207 5.7333C7.36895 5.48018 6.97519 5.48018 6.72207 5.7333C6.46895 5.98643 6.46895 6.38018 6.72207 6.6333L8.46582 8.40518H3.45957C3.12207 8.40518 2.84082 8.68643 2.84082 9.02393C2.84082 9.36143 3.12207 9.64268 3.45957 9.64268H8.49395L6.72207 11.4427C6.46895 11.6958 6.46895 12.0896 6.72207 12.3427C6.83457 12.4552 7.00332 12.5114 7.17207 12.5114C7.34082 12.5114 7.50957 12.4552 7.62207 12.3145L10.4346 9.4458C10.6877 9.24893 10.6877 8.85518 10.4346 8.60205Z"*/}
{/* fill=""*/}
{/* />*/}
{/* </g>*/}
{/* <defs>*/}
{/* <clipPath id="clip0_130_9814">*/}
{/* <rect*/}
{/* width="18"*/}
{/* height="18"*/}
{/* fill="white"*/}
{/* transform="translate(0 0.052124)"*/}
{/* />*/}
{/* </clipPath>*/}
{/* </defs>*/}
{/* </svg>*/}
{/* Authentication*/}
{/* <svg*/}
{/* className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${*/}
{/* open && 'rotate-180'*/}
{/* }`}*/}
{/* width="20"*/}
{/* height="20"*/}
{/* viewBox="0 0 20 20"*/}
{/* fill="none"*/}
{/* xmlns="http://www.w3.org/2000/svg"*/}
{/* >*/}
{/* <path*/}
{/* fillRule="evenodd"*/}
{/* clipRule="evenodd"*/}
{/* d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"*/}
{/* fill=""*/}
{/* />*/}
{/* </svg>*/}
{/* </NavLink>*/}
{/* */}{/*{/* <!-- Dropdown Menu Start --> */}
{/* <div*/}
{/* className={`translate transform overflow-hidden ${*/}
{/* !open && 'hidden'*/}
{/* }`}*/}
{/* >*/}
{/* <ul className="mt-4 mb-5.5 flex flex-col gap-2.5 pl-6">*/}
{/* <li>*/}
{/* <NavLink*/}
{/* to="/auth/signin"*/}
{/* className={({ isActive }) =>*/}
{/* 'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +*/}
{/* (isActive && '!text-white')*/}
{/* }*/}
{/* >*/}
{/* Sign In*/}
{/* </NavLink>*/}
{/* </li>*/}
{/* <li>*/}
{/* <NavLink*/}
{/* to="/auth/signup"*/}
{/* className={({ isActive }) =>*/}
{/* 'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +*/}
{/* (isActive && '!text-white')*/}
{/* }*/}
{/* >*/}
{/* Sign Up*/}
{/* </NavLink>*/}
{/* </li>*/}
{/* </ul>*/}
{/* </div>*/}
{/* */}{/*{/* <!-- Dropdown Menu End --> */}
{/* </React.Fragment>*/}
{/* );*/}
{/* }}*/}
{/* </SidebarLinkGroup>*/}
{/* */}{/*{/* <!-- Menu Item Auth Pages --> */}
{/* </ul>*/}
{/*</div>}*/}
</nav>
{/* <!-- Sidebar Menu --> */}
</div>
</aside>
);
};

View File

@ -1,20 +1,14 @@
import { ReactNode, useState } from "react"; import { ReactNode, useState } from 'react';
interface SidebarLinkGroupProps interface SidebarLinkGroupProps {
{
children: (handleClick: () => void, open: boolean) => ReactNode; children: (handleClick: () => void, open: boolean) => ReactNode;
activeCondition: boolean; activeCondition: boolean;
} }
const SidebarLinkGroup = ({ const SidebarLinkGroup = ({ children, activeCondition }: SidebarLinkGroupProps) => {
children,
activeCondition,
}: SidebarLinkGroupProps) =>
{
const [open, setOpen] = useState<boolean>(activeCondition); const [open, setOpen] = useState<boolean>(activeCondition);
const handleClick = () => const handleClick = () => {
{
setOpen(!open); setOpen(!open);
}; };

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,14 @@
import { useEffect } from "react"; import React, { useEffect } from 'react';
import { useLocation } from "react-router-dom"; import { useLocation } from 'react-router-dom';
interface PageTitleProps interface PageTitleProps {
{
title: string; title: string;
} }
export const PageTitle: React.FC<PageTitleProps> = ({ title }) => export const PageTitle: React.FC<PageTitleProps> = ({ title }) => {
{
const location = useLocation(); const location = useLocation();
useEffect(() => useEffect(() => {
{
document.title = title; document.title = title;
}, [location, title]); }, [location, title]);

View File

@ -1,11 +1,10 @@
import { ChangeEventHandler } from "react"; import { ChangeEventHandler } from 'react';
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
export const Search = ({ searchItem, handleInputChange }: { export const Search = ({ searchItem, handleInputChange }: {
searchItem: string; searchItem: string;
handleInputChange: ChangeEventHandler handleInputChange: ChangeEventHandler
}) => }) => {
{
const { t } = useTranslation(); const { t } = useTranslation();
return <div return <div
className="col-span-12 rounded-sm border border-stroke bg-white px-5 pt-7.5 pb-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-8"> className="col-span-12 rounded-sm border border-stroke bg-white px-5 pt-7.5 pb-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:col-span-8">
@ -15,7 +14,7 @@ export const Search = ({ searchItem, handleInputChange }: {
type="text" type="text"
onChange={handleInputChange} onChange={handleInputChange}
value={searchItem} value={searchItem}
placeholder={ t("logs.search") } placeholder={t('logs.search')}
/> />
</div> </div>
</div>; </div>;

View File

@ -1,20 +1,16 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import { TLeaderboardRecord } from "../../../types/leaderboard.ts"; import { TLeaderboardRecord } from '../../../types/leaderboard.ts';
import { ContentLoader } from "../../mini/loaders/ContentLoader.tsx"; import { ContentLoader } from '../../mini/loaders/ContentLoader.tsx';
import { WarningAlert } from '../../mini/alerts/Warning.tsx';
import { WarningAlert } from "../../mini/alerts/Warning.tsx"; export const StationTable = ({ stations, error }: { stations: TLeaderboardRecord[], error: number }) => {
export const StationTable = ({ stations, error }: { stations: TLeaderboardRecord[], error: number }) =>
{
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
{error === 2 && <WarningAlert title={t('content_loader.notfound.header')}
{ error === 2 && <WarningAlert title={ t("content_loader.notfound.header") } description={t('content_loader.notfound.description')} />}
description={ t("content_loader.notfound.description") }/> }
{error === 0 && <ContentLoader />} {error === 0 && <ContentLoader />}
{error === 1 && <div {error === 1 && <div
className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
@ -23,17 +19,17 @@ export const StationTable = ({ stations, error }: { stations: TLeaderboardRecord
<div className="grid grid-cols-2 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-3"> <div className="grid grid-cols-2 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-3">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.user") } {t('leaderboard.user')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.time") } {t('leaderboard.time')}
</h5> </h5>
</div> </div>
<div className="hidden p-2.5 text-center sm:block xl:p-5"> <div className="hidden p-2.5 text-center sm:block xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.actions") } {t('leaderboard.actions')}
</h5> </h5>
</div> </div>
</div> </div>
@ -41,14 +37,14 @@ export const StationTable = ({ stations, error }: { stations: TLeaderboardRecord
{stations.map((station, key) => ( {stations.map((station, key) => (
<div <div
className={`grid grid-cols-2 sm:grid-cols-3 ${stations.length === (key + 1) // todo: ... className={`grid grid-cols-2 sm:grid-cols-3 ${stations.length === (key + 1) // todo: ...
? "" ? ''
: "border-b border-stroke dark:border-strokedark" : 'border-b border-stroke dark:border-strokedark'
}`} }`}
key={station.id} key={station.id}
> >
<div className="flex justify-center items-center gap-3 p-5 lg:p-5"> <div className="flex justify-center items-center gap-3 p-5 lg:p-5">
<p className="text-black dark:text-white sm:block break-all"> <p className="text-black dark:text-white sm:block break-all">
<Link to={ "/profile/" + station.steam } <Link to={'/profile/' + station.steam}
className="color-orchid">{station.steamName}</Link> className="color-orchid">{station.steamName}</Link>
</p> </p>
</div> </div>
@ -59,10 +55,10 @@ export const StationTable = ({ stations, error }: { stations: TLeaderboardRecord
<div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5"> <div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5">
<Link <Link
to={ "/profile/" + station.steam } to={'/profile/' + station.steam}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("leaderboard.profile") } {t('leaderboard.profile')}
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,18 +1,17 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import { TLeaderboardRecord } from "../../../types/leaderboard.ts"; import { TLeaderboardRecord } from '../../../types/leaderboard.ts';
import { ContentLoader } from "../../mini/loaders/ContentLoader.tsx"; import { ContentLoader } from '../../mini/loaders/ContentLoader.tsx';
import { WarningAlert } from "../../mini/alerts/Warning.tsx"; import { WarningAlert } from '../../mini/alerts/Warning.tsx';
export const TrainTable = ({ trains, error }: { trains: TLeaderboardRecord[], error: number }) => export const TrainTable = ({ trains, error }: { trains: TLeaderboardRecord[], error: number }) => {
{
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
{ error === 2 && <WarningAlert title={ t("content_loader.notfound.header") } {error === 2 && <WarningAlert title={t('content_loader.notfound.header')}
description={ t("content_loader.notfound.description") }/> } description={t('content_loader.notfound.description')} />}
{error === 0 && <ContentLoader />} {error === 0 && <ContentLoader />}
{error === 1 && <div {error === 1 && <div
className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
@ -20,27 +19,27 @@ export const TrainTable = ({ trains, error }: { trains: TLeaderboardRecord[], er
<div className="grid grid-cols-4 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-5"> <div className="grid grid-cols-4 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-5">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.user") } {t('leaderboard.user')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.points") } {t('leaderboard.points')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.distance") } {t('leaderboard.distance')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center sm:block xl:p-5"> <div className="p-2.5 text-center sm:block xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.time") } {t('leaderboard.time')}
</h5> </h5>
</div> </div>
<div className="hidden p-2.5 text-center sm:block xl:p-5"> <div className="hidden p-2.5 text-center sm:block xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("leaderboard.actions") } {t('leaderboard.actions')}
</h5> </h5>
</div> </div>
</div> </div>
@ -48,14 +47,14 @@ export const TrainTable = ({ trains, error }: { trains: TLeaderboardRecord[], er
{trains.map((train, key) => ( {trains.map((train, key) => (
<div <div
className={`grid grid-cols-4 sm:grid-cols-5 ${trains.length === (key + 1) className={`grid grid-cols-4 sm:grid-cols-5 ${trains.length === (key + 1)
? "" ? ''
: "border-b border-stroke dark:border-strokedark" : 'border-b border-stroke dark:border-strokedark'
}`} }`}
key={train.id} key={train.id}
> >
<div className="flex items-center justify-center gap-3 p-5 lg:p-5"> <div className="flex items-center justify-center gap-3 p-5 lg:p-5">
<p className="text-black dark:text-white sm:block break-all"> <p className="text-black dark:text-white sm:block break-all">
<Link to={ "/profile/" + train.steam } <Link to={'/profile/' + train.steam}
className="color-orchid">{train.steamName}</Link> className="color-orchid">{train.steamName}</Link>
</p> </p>
</div> </div>
@ -74,10 +73,10 @@ export const TrainTable = ({ trains, error }: { trains: TLeaderboardRecord[], er
<div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5"> <div className="hidden items-center justify-center p-2.5 sm:flex xl:p-5">
<Link <Link
to={ "/profile/" + train.steam } to={'/profile/' + train.steam}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("leaderboard.profile") } {t('leaderboard.profile')}
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,25 +1,23 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { TLogStationData } from "../../../types/log.ts"; import { TLogStationData } from '../../../types/log.ts';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import { toast } from "react-toastify"; import { toast } from 'react-toastify';
export const StationLog = ({ data }: { data: TLogStationData }) =>
{
const copyLink = () =>
{
void navigator.clipboard.writeText(location.href);
toast.success("Skopiowano link do schowka!");
};
const report = () =>
{
toast.info("Do schowka skopiowano dane wyjścia z posterunku, możesz ich użyć do wysłania na kanale #multiplayer-help-requests na oficjalnym serwerze Discord gry simrail.", {
autoClose: 5000,
});
void navigator.clipboard.writeText(`;user: \`${ data.userUsername }\`\n;steam: \`${ data.userSteamId }\`\n;left: <t:${ data.leftDate }>${ data.joinedDate ? `\n;joined: <t:${ data.joinedDate }>` : "" }\n;station: \`${ data.stationName }\`\n;link: ${ location.href }\n\n`);
};
export const StationLog = ({ data }: { data: TLogStationData }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const copyLink = () => {
void navigator.clipboard.writeText(location.href);
toast.success(t('log.toasts.copied'));
};
const report = () => {
toast.info(t('log.toasts.report'), {
autoClose: 5000
});
void navigator.clipboard.writeText(`;user: \`${data.userUsername}\`\n;steam: \`${data.userSteamId}\`\n;left: <t:${data.leftDate}>${data.joinedDate ? `\n;joined: <t:${data.joinedDate}>` : ''}\n;station: \`${data.stationName}\`\n;link: ${location.href}\n\n`);
};
return <div return <div
className="overflow-hidden rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark"> className="overflow-hidden rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="px-4 pt-6 text-center lg:pb-8 xl:pb-11.5"> <div className="px-4 pt-6 text-center lg:pb-8 xl:pb-11.5">
@ -33,46 +31,28 @@ export const StationLog = ({ data }: { data: TLogStationData }) =>
<h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white"> <h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white">
{data.userUsername} {data.userUsername}
</h3> </h3>
{/*<div*/ }
{/* 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]">*/ }
{/* <div*/ }
{/* className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row">*/ }
{/* <span className="font-semibold text-black dark:text-white">*/ }
{/* { Math.floor(data.player.trainDistance / 1000) }km*/ }
{/* </span>*/ }
{/* <span className="text-sm text-wrap">{ t("profile.stats.distance") }</span>*/ }
{/* </div>*/ }
{/* <div*/ }
{/* className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row">*/ }
{/* <span className="font-semibold text-black dark:text-white">*/ }
{/* { Math.floor(data.player.dispatcherTime / 3600000) }h*/ }
{/* </span>*/ }
{/* <span className="text-sm text-wrap">{ t("profile.stats.time") }</span>*/ }
{/* </div>*/ }
{/*</div>*/ }
</div> </div>
</div> </div>
<div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5"> <div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5">
<div className="flex flex-col sm:flex-row sm:flex-wrap sm:justify-end"> <div className="flex flex-col sm:flex-row sm:flex-wrap sm:justify-end">
<div className="flex flex-col"> <div className="flex flex-col">
<h1 className="text-xl text-black dark:text-white pb-5">{ t("log.station.header") }</h1> <h1 className="text-xl text-black dark:text-white pb-5">{t('log.station.header')}</h1>
<p>{ t("log.station.server", { server: data.server.toUpperCase() }) }</p> <p>{t('log.station.server', { server: data.server.toUpperCase() })}</p>
<p>{ t("log.station.station", { name: data.stationName, short: data.stationShort }) }</p> <p>{t('log.station.station', { name: data.stationName, short: data.stationShort })}</p>
{data.joinedDate && {data.joinedDate &&
<p>{ t("log.station.joined", { date: dayjs(data.joinedDate).format("DD/MM/YYYY HH:mm") }) }</p> } <p>{t('log.station.joined', { date: dayjs(data.joinedDate).format('DD/MM/YYYY HH:mm') })}</p>}
<p>{ t("log.station.left", { date: dayjs(data.leftDate).format("DD/MM/YYYY HH:mm") }) }</p> <p>{t('log.station.left', { date: dayjs(data.leftDate).format('DD/MM/YYYY HH:mm') })}</p>
{data.joinedDate && {data.joinedDate &&
<p>{ t("log.station.spent", { date: dayjs.duration(data.leftDate - data.joinedDate).format("H[h] m[m]") }) }</p> } <p>{t('log.station.spent', { date: dayjs.duration(data.leftDate - data.joinedDate).format('H[h] m[m]') })}</p>}
</div> </div>
<div className="flex flex-col gap-5 mt-5 sm:mt-0 sm:ml-auto"> <div className="flex flex-col gap-5 mt-5 sm:mt-0 sm:ml-auto">
<a <a
onClick={report} onClick={report}
className="cursor-pointer inline-flex items-center justify-center rounded-md bg-meta-7 py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10" className="cursor-pointer inline-flex items-center justify-center rounded-md bg-meta-7 py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
> >
Zgłoś {t('log.buttons.report')}
</a> </a>
<a <a
@ -80,7 +60,7 @@ export const StationLog = ({ data }: { data: TLogStationData }) =>
className="cursor-pointer inline-flex items-center justify-center rounded-md bg-primary py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10" className="cursor-pointer inline-flex items-center justify-center rounded-md bg-primary py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
> >
Kopiuj link {t('log.buttons.copy')}
</a> </a>
</div> </div>
</div> </div>

View File

@ -0,0 +1,73 @@
import { useTranslation } from 'react-i18next';
import { TLogTrainData } from '../../../types/log.ts';
import dayjs from 'dayjs';
import { toast } from 'react-toastify';
export const TrainLog = ({ data }: { data: TLogTrainData }) => {
const { t } = useTranslation();
const copyLink = () => {
void navigator.clipboard.writeText(location.href);
toast.success(t('log.toasts.copied'));
};
const report = () => {
toast.info(t('log.toasts.report'), {
autoClose: 5000
});
void navigator.clipboard.writeText(`;user: \`${data.userUsername}\`\n;steam: \`${data.userSteamId}\`\n;left: <t:${data.leftDate}>${data.joinedDate ? `\n;joined: <t:${data.joinedDate}>` : ''}\n;train: \`${data.trainNumber}\`\n;link: ${location.href}\n\n`);
};
return <div
className="overflow-hidden rounded-sm border border-stroke bg-white shadow-default dark:border-strokedark dark:bg-boxdark">
<div className="px-4 pt-6 text-center lg:pb-8 xl:pb-11.5">
<div
className="mx-auto w-full max-w-30 rounded-full bg-white/20 p-1 backdrop-blur sm:h-44 sm:max-w-44 sm:p-3">
<div className="relative drop-shadow-2">
<img className="rounded-full" src={data.userAvatar} alt="profile" />
</div>
</div>
<div className="mt-4">
<h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white">
{data.userUsername}
</h3>
</div>
</div>
<div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5">
<div className="flex flex-col sm:flex-row sm:flex-wrap sm:justify-end">
<div className="flex flex-col">
<h1 className="text-xl text-black dark:text-white pb-5">{t('log.train.header')}</h1>
<p>{t('log.train.server', { server: data.server.toUpperCase() })}</p>
<p>{t('log.train.train', { name: data.trainName, number: data.trainNumber })}</p>
{(data.distance || data.distance === 0) &&
<p>{t('log.train.distance', { distance: (data.distance / 1000).toFixed(2) })}</p>}
{(data.points || data.points === 0) && <p>{t('log.train.points', { points: data.points })}</p>}
{data.joinedDate &&
<p>{t('log.train.joined', { date: dayjs(data.joinedDate).format('DD/MM/YYYY HH:mm') })}</p>}
<p>{t('log.train.left', { date: dayjs(data.leftDate).format('DD/MM/YYYY HH:mm') })}</p>
{data.joinedDate &&
<p>{t('log.train.spent', { date: dayjs.duration(data.leftDate - data.joinedDate).format('H[h] m[m]') })}</p>}
</div>
<div className="flex flex-col gap-5 mt-5 sm:mt-0 sm:ml-auto">
<a
onClick={report}
className="cursor-pointer inline-flex items-center justify-center rounded-md bg-meta-7 py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
>
{t('log.buttons.report')}
</a>
<a
onClick={copyLink}
className="cursor-pointer inline-flex items-center justify-center rounded-md bg-primary py-4 px-10 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
>
{t('log.buttons.copy')}
</a>
</div>
</div>
</div>
</div>;
};

View File

@ -1,22 +1,21 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import { ContentLoader } from "../../mini/loaders/ContentLoader.tsx"; import { ContentLoader } from '../../mini/loaders/ContentLoader.tsx';
import { TStationRecord } from "../../../types/station.ts"; import { TStationRecord } from '../../../types/station.ts';
import { WarningAlert } from "../../mini/alerts/Warning.tsx"; import { WarningAlert } from '../../mini/alerts/Warning.tsx';
// setSearchItem: Dispatch<SetStateAction<string>> // setSearchItem: Dispatch<SetStateAction<string>>
export const StationTable = ({ stations, error }: { export const StationTable = ({ stations, error }: {
stations: TStationRecord[], error: number stations: TStationRecord[], error: number
}) => }) => {
{
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
{ error === 2 && <WarningAlert title={ t("content_loader.notfound.header") } {error === 2 && <WarningAlert title={t('content_loader.notfound.header')}
description={ t("content_loader.notfound.description") }/> } description={t('content_loader.notfound.description')} />}
{error === 0 && <ContentLoader />} {error === 0 && <ContentLoader />}
{error === 1 && <div {error === 1 && <div
@ -25,22 +24,22 @@ export const StationTable = ({ stations, error }: {
<div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-4"> <div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-4">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.user") } {t('logs.user')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.station") } {t('logs.station')}
</h5> </h5>
</div> </div>
<div className="hidden sm:block p-2.5 text-center xl:p-5"> <div className="hidden sm:block p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.time") } {t('logs.time')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.actions") } {t('logs.actions')}
</h5> </h5>
</div> </div>
</div> </div>
@ -48,39 +47,39 @@ export const StationTable = ({ stations, error }: {
{stations.map((station, key) => ( {stations.map((station, key) => (
<div <div
className={`grid grid-cols-3 sm:grid-cols-4 ${stations.length === (key + 1) className={`grid grid-cols-3 sm:grid-cols-4 ${stations.length === (key + 1)
? "" ? ''
: "border-b border-stroke dark:border-strokedark" : 'border-b border-stroke dark:border-strokedark'
}`} }`}
key={station.id} key={station.id}
> >
<div className="flex items-center justify-center gap-3 p-2.5 lg:p-5"> <div className="flex items-center justify-center gap-3 p-2.5 lg:p-5">
<p className="text-black dark:text-white sm:block break-all"> <p className="text-black dark:text-white sm:block break-all">
<Link to={ "/profile/" + station.userSteamId } <Link to={'/profile/' + station.userSteamId}
className="color-orchid">{station.userUsername}</Link> className="color-orchid">{station.userUsername}</Link>
</p> </p>
</div> </div>
<div className="flex items-center justify-center p-2.5 lg:p-5"> <div className="flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-6 sm:block break-all">{ station.server.toUpperCase() } - { station.stationName ?? "--" }</p> <p className="text-meta-6 sm:block break-all">{station.server.toUpperCase()} - {station.stationName ?? '--'}</p>
</div> </div>
<div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5"> <div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-3">{ dayjs(station.leftDate).format("HH:mm DD/MM/YYYY") }</p> <p className="text-meta-3">{dayjs(station.leftDate).format('HH:mm DD/MM/YYYY')}</p>
</div> </div>
<div <div
className="items-center justify-center p-2.5 flex xl:p-5 gap-2 flex-wrap sm:flex-nowrap "> className="items-center justify-center p-2.5 flex xl:p-5 gap-2 flex-wrap sm:flex-nowrap ">
<Link <Link
to={ "/profile/" + station.userSteamId } to={'/profile/' + station.userSteamId}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("logs.profile") } {t('logs.profile')}
</Link> </Link>
<Link <Link
to={ "/log/" + station.id } to={'/log/' + station.id}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("logs.record") } {t('logs.record')}
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,22 +1,21 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import { TTrainRecord } from "../../../types/train.ts"; import { TTrainRecord } from '../../../types/train.ts';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import { ContentLoader } from "../../mini/loaders/ContentLoader.tsx"; import { ContentLoader } from '../../mini/loaders/ContentLoader.tsx';
import { WarningAlert } from "../../mini/alerts/Warning.tsx"; import { WarningAlert } from '../../mini/alerts/Warning.tsx';
// setSearchItem: Dispatch<SetStateAction<string>> // setSearchItem: Dispatch<SetStateAction<string>>
export const TrainTable = ({ trains, error }: { export const TrainTable = ({ trains, error }: {
trains: TTrainRecord[], error: number trains: TTrainRecord[], error: number
}) => }) => {
{
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <>
{ error === 2 && <WarningAlert title={ t("content_loader.notfound.header") } {error === 2 && <WarningAlert title={t('content_loader.notfound.header')}
description={ t("content_loader.notfound.description") }/> } description={t('content_loader.notfound.description')} />}
{error === 0 && <ContentLoader />} {error === 0 && <ContentLoader />}
{error === 1 && <div {error === 1 && <div
className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> className="rounded-sm border border-stroke bg-white px-5 pt-6 pb-2.5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
@ -24,32 +23,32 @@ export const TrainTable = ({ trains, error }: {
<div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-6"> <div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-6">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.user") } {t('logs.user')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.train") } {t('logs.train')}
</h5> </h5>
</div> </div>
<div className="hidden sm:block p-2.5 text-center xl:p-5"> <div className="hidden sm:block p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.points") } {t('logs.points')}
</h5> </h5>
</div> </div>
<div className="hidden sm:block p-2.5 text-center xl:p-5"> <div className="hidden sm:block p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.distance") } {t('logs.distance')}
</h5> </h5>
</div> </div>
<div className="hidden sm:block p-2.5 text-center xl:p-5"> <div className="hidden sm:block p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.time") } {t('logs.time')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("logs.actions") } {t('logs.actions')}
</h5> </h5>
</div> </div>
</div> </div>
@ -57,47 +56,47 @@ export const TrainTable = ({ trains, error }: {
{trains.map((train, key) => ( {trains.map((train, key) => (
<div <div
className={`grid grid-cols-3 sm:grid-cols-6 ${trains.length === (key + 1) className={`grid grid-cols-3 sm:grid-cols-6 ${trains.length === (key + 1)
? "" ? ''
: "border-b border-stroke dark:border-strokedark" : 'border-b border-stroke dark:border-strokedark'
}`} }`}
key={train.id} key={train.id}
> >
<div className="flex items-center justify-center gap-3 p-2.5 lg:p-5"> <div className="flex items-center justify-center gap-3 p-2.5 lg:p-5">
<p className="text-black dark:text-white sm:block break-all"> <p className="text-black dark:text-white sm:block break-all">
<Link to={ "/profile/" + train.userSteamId } <Link to={'/profile/' + train.userSteamId}
className="color-orchid">{train.userUsername}</Link> className="color-orchid">{train.userUsername}</Link>
</p> </p>
</div> </div>
<div className="flex items-center justify-center p-2.5 lg:p-5"> <div className="flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-6 sm:block break-all">{ train.server.toUpperCase() } - { train.trainNumber ?? "--" }</p> <p className="text-meta-6 sm:block break-all">{train.server.toUpperCase()} - {train.trainNumber ?? '--'}</p>
</div> </div>
<div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5"> <div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-6">{ train.distance ? train.points : "--" }</p> <p className="text-meta-6">{train.distance ? train.points : '--'}</p>
</div> </div>
<div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5"> <div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-5">{ train.distance ? `${ (train.distance / 1000).toFixed(2) }km` : "--" }</p> <p className="text-meta-5">{train.distance ? `${(train.distance / 1000).toFixed(2)}km` : '--'}</p>
</div> </div>
<div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5"> <div className="hidden sm:flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-3">{ dayjs(train.leftDate).format("HH:mm DD/MM/YYYY") }</p> <p className="text-meta-3">{dayjs(train.leftDate).format('HH:mm DD/MM/YYYY')}</p>
</div> </div>
<div <div
className="items-center justify-center p-2.5 flex xl:p-5 gap-2 flex-wrap sm:flex-nowrap "> className="items-center justify-center p-2.5 flex xl:p-5 gap-2 flex-wrap sm:flex-nowrap ">
<Link <Link
to={ "/profile/" + train.userSteamId } to={'/profile/' + train.userSteamId}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("logs.profile") } {t('logs.profile')}
</Link> </Link>
<Link <Link
to={ "/log/" + train.id } to={'/log/' + train.id}
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-5 text-center font-medium text-white hover:bg-opacity-50 lg:px-4 xl:px-5"
> >
{ t("logs.record") } {t('logs.record')}
</Link> </Link>
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from 'react';
import { TProfileData } from "../../../types/profile.ts"; import { TProfileData } from '../../../types/profile.ts';
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { ArrowIcon } from '../../mini/icons/ArrowIcon.tsx';
export const ProfileCard = ({ data }: { data: TProfileData }) => export const ProfileCard = ({ data }: { data: TProfileData }) => {
{
const [showTrains, setShowTrains] = useState(false); const [showTrains, setShowTrains] = useState(false);
const [showStations, setShowStations] = useState(false); const [showStations, setShowStations] = useState(false);
@ -30,14 +30,14 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
<span className="font-semibold text-black dark:text-white"> <span className="font-semibold text-black dark:text-white">
{Math.floor(data.player.trainDistance / 1000)}km {Math.floor(data.player.trainDistance / 1000)}km
</span> </span>
<span className="text-sm text-wrap">{ t("profile.stats.distance") }</span> <span className="text-sm text-wrap">{t('profile.stats.distance')}</span>
</div> </div>
<div <div
className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row"> className="flex flex-col items-center justify-center gap-1 border-r border-stroke px-4 dark:border-strokedark xsm:flex-row">
<span className="font-semibold text-black dark:text-white"> <span className="font-semibold text-black dark:text-white">
{Math.floor(data.player.dispatcherTime / 3600000)}h {Math.floor(data.player.dispatcherTime / 3600000)}h
</span> </span>
<span className="text-sm text-wrap">{ t("profile.stats.time") }</span> <span className="text-sm text-wrap">{t('profile.stats.time')}</span>
</div> </div>
</div> </div>
</div> </div>
@ -45,51 +45,36 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
{Object.keys(data.player.trainStats || {}).length > 0 && {Object.keys(data.player.trainStats || {}).length > 0 &&
<div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5"> <div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5">
<div className="group relative cursor-pointer" onClick={() => setShowTrains(val => !val)}> <div className="group relative cursor-pointer" onClick={() => setShowTrains(val => !val)}>
<h1 className="text-xl text-black dark:text-white pb-5">{ t("profile.trains.header") }</h1> <h1 className="text-xl text-black dark:text-white pb-5">{t('profile.trains.header')}</h1>
<svg <ArrowIcon rotated={showTrains} />
className={ `absolute right-4 top-1/2 -translate-y-1/2 fill-current ${ showTrains && "rotate-180"
}` }
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"
fill=""
/>
</svg>
</div> </div>
{ showTrains && <div className="flex flex-col rounded-sm border border-stroke dark:border-strokedark"> {showTrains &&
<div className="flex flex-col rounded-sm border border-stroke dark:border-strokedark">
<div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-4"> <div className="grid grid-cols-3 rounded-sm bg-gray-2 dark:bg-meta-4 sm:grid-cols-4">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.train") } {t('profile.trains.train')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.distance") } {t('profile.trains.distance')}
</h5> </h5>
</div> </div>
<div className="hidden sm:block p-2.5 text-center xl:p-5"> <div className="hidden sm:block p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.points") } {t('profile.trains.points')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.time") } {t('profile.trains.time')}
</h5> </h5>
</div> </div>
</div> </div>
{ Object.keys(data.player.trainStats).map(trainName => {Object.keys(data.player.trainStats).map(trainName => {
{
const train = data.player.trainStats[trainName]; const train = data.player.trainStats[trainName];
return <div return <div
@ -123,40 +108,25 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
{Object.keys(data.player.dispatcherStats || {}).length > 0 && {Object.keys(data.player.dispatcherStats || {}).length > 0 &&
<div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5"> <div className="bg-white px-5 pt-6 pb-5 shadow-default dark:bg-boxdark sm:px-7.5">
<div className="group relative cursor-pointer" onClick={() => setShowStations(val => !val)}> <div className="group relative cursor-pointer" onClick={() => setShowStations(val => !val)}>
<h1 className="text-xl text-black dark:text-white pb-5">{ t("profile.stations.header") }</h1> <h1 className="text-xl text-black dark:text-white pb-5">{t('profile.stations.header')}</h1>
<svg <ArrowIcon rotated={showTrains} />
className={ `absolute right-4 top-1/2 -translate-y-1/2 fill-current ${ showStations && "rotate-180"
}` }
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"
fill=""
/>
</svg>
</div> </div>
{ showStations && <div className="flex flex-col rounded-sm border border-stroke dark:border-strokedark"> {showStations &&
<div className="flex flex-col rounded-sm border border-stroke dark:border-strokedark">
<div className="grid grid-cols-2 rounded-sm bg-gray-2 dark:bg-meta-4"> <div className="grid grid-cols-2 rounded-sm bg-gray-2 dark:bg-meta-4">
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.stations.station") } {t('profile.stations.station')}
</h5> </h5>
</div> </div>
<div className="p-2.5 text-center xl:p-5"> <div className="p-2.5 text-center xl:p-5">
<h5 className="text-sm font-medium uppercase xsm:text-base"> <h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.stations.time") } {t('profile.stations.time')}
</h5> </h5>
</div> </div>
</div> </div>
{ Object.keys(data.player.dispatcherStats).map(stationName => {Object.keys(data.player.dispatcherStats).map(stationName => {
{
const station = data.player.dispatcherStats[stationName]; const station = data.player.dispatcherStats[stationName];
return <div return <div
className={`grid grid-cols-2 border-t border-t-stroke dark:border-t-strokedark`} className={`grid grid-cols-2 border-t border-t-stroke dark:border-t-strokedark`}

View File

@ -13,13 +13,17 @@ const resources = {
}, },
}; };
i18n void i18n
.use(initReactI18next) .use(initReactI18next)
.use(LanguageDetector) .use(LanguageDetector)
.init({ .init({
resources, resources,
debug: false, debug: false,
fallbackLng: "en", fallbackLng: {
"pl-PL": [ "pl" ],
default: [ "en" ],
},
interpolation: { interpolation: {
escapeValue: false, escapeValue: false,
}, },

View File

@ -1 +1,134 @@
{} {
"_": {
"title": "Simrail Stats"
},
"preview": {
"title": "Preview version!",
"description": "The site is in version V3-PREVIEW, may contain errors. I will be grateful for reporting all errors on the project page (git.alekswilc.dev) or in a private message on discord - alekswilc. Stay tuned!"
},
"home": {
"stats": {
"trains": "Trains",
"dispatchers": "Dispatchers",
"profiles": "Profiles"
},
"title": "Simrail Stats",
"description": "The most complete SimRail logs and statistics site!",
"buttons": {
"project": "Project page",
"forum": "Forum Page"
},
"footer": {
"license": "License:",
"powered": "Based on:",
"thanks": "Special thanks to <bahu>BAHU.PRO hosting</bahu>, <simrailelite>Simrail ELITE discord</simrailelite>, Simrail community and my girlfriend",
"author": "Created by <anchor>{{author}}</anchor> with ❤️ for the Simrail community"
}
},
"leaderboard": {
"user": "Player",
"time": "Time",
"distance": "Distance",
"points": "Points",
"profile": "Profile",
"actions": "Actions"
},
"logs": {
"user": "Player",
"time": "Time",
"distance": "Distance",
"points": "Points",
"profile": "Profile",
"record": "Record",
"train": "Train",
"actions": "Actions",
"search": "Type to search",
"station": "Station"
},
"content_loader": {
"error": {
"header": "Page loading error",
"description": "Check your internet connection and refresh the page",
"report": "Report a bug",
"refresh": "Refresh"
},
"notfound": {
"header": "No data found",
"description": "Your search returned no results."
}
},
"profile": {
"stats": {
"distance": "Kilometers traveled",
"time": "Dispatcher hours"
},
"trains": {
"header": "Train Statistics",
"train": "Train",
"distance": "Distance",
"points": "Points",
"time": "Time"
},
"stations": {
"header": "Station Statistics",
"station": "Station",
"time": "Time"
},
"errors": {
"notfound": {
"title": "Profile not found",
"description": "Player's profile could not be found or the player has a private Steam profile."
},
"blacklist": {
"title": "Unable to display profile",
"description": "This player's profile has been blocked."
}
}
},
"log": {
"errors": {
"title": "Record not found",
"description": "This record could not be found."
},
"blacklist": {
"title": "The record cannot be displayed",
"description": "The record has been blocked."
},
"station": {
"header": "Leaving the station",
"server": "Server: {{server}}",
"station": "Station: {{name}} - {{short}}",
"joined": "Date of entry: {{date}}",
"left": "Date of exit: {{date}}",
"spent": "Time spent: {{date}}"
},
"train": {
"header": "Leaving the train",
"server": "Server: {{server}}",
"train": "Train: {{name}} {{number}}",
"joined": "Date of entry: {{date}}",
"left": "Date of exit: {{date}}",
"spent": "Time spent: {{date}}",
"distance": "Distance: {{distance}}km",
"points": "Points: {{points}}"
},
"toasts": {
"copied": "Link copied to clipboard!",
"report": "The outpost exit data has been copied to your clipboard, you can use it to submit to the #multiplayer-help-requests channel on the official simrail Discord server. Don't forget to add a reason for the report!"
},
"buttons": {
"report": "Report",
"copy": "Copy link"
}
},
"sidebar": {
"home": "Home page",
"logs": "Logs",
"stations": "Stations",
"trains": "Trains",
"leaderboard": "Leaderboard",
"info": "INFO",
"admin": "ADMIN"
},
"admin": {}
}

View File

@ -2,10 +2,14 @@
"_": { "_": {
"title": "Simrail Stats" "title": "Simrail Stats"
}, },
"preview": {
"title": "Wersja preview!",
"description": "Strona znajduje się w wersji V3-PREVIEW, może zawierać błędy. Będe wdzieczny za zgłaszanie wszystkich błędów na stronie projektu (git.alekswilc.dev) lub w wiadomości prywatnej na discordzie - alekswilc. Stay tuned!"
},
"home": { "home": {
"stats": { "stats": {
"trains": "Pociągi", "trains": "Pociągów",
"dispatchers": "Dyżurni ruchu", "dispatchers": "Dyżurnych ruchu",
"profiles": "Profili" "profiles": "Profili"
}, },
"title": "Simrail Stats", "title": "Simrail Stats",
@ -21,13 +25,6 @@
"author": "Stworzone przez <anchor>{{author}}</anchor> z ❤️ dla społeczności Simrail" "author": "Stworzone przez <anchor>{{author}}</anchor> z ❤️ dla społeczności Simrail"
} }
}, },
"pages": {
"home": "Strona główna",
"logs": "Logi",
"stations": "Stacje",
"trains": "Pociągi",
"leaderboard": "Tablica wyników"
},
"leaderboard": { "leaderboard": {
"user": "Gracz", "user": "Gracz",
"time": "Czas", "time": "Czas",
@ -104,6 +101,34 @@
"joined": "Data wejścia: {{date}}", "joined": "Data wejścia: {{date}}",
"left": "Data wyjścia: {{date}}", "left": "Data wyjścia: {{date}}",
"spent": "Spędzony czas: {{date}}" "spent": "Spędzony czas: {{date}}"
},
"train": {
"header": "Wyjście z pociągu",
"server": "Serwer: {{server}}",
"train": "Pociąg: {{name}} {{number}}",
"joined": "Data wejścia: {{date}}",
"left": "Data wyjścia: {{date}}",
"spent": "Spędzony czas: {{date}}",
"distance": "Dystans: {{distance}}km",
"points": "Punkty: {{points}}"
},
"toasts": {
"copied": "Skopiowano link do schowka!",
"report": "Do schowka skopiowano dane wyjścia z posterunku, możesz ich użyć do wysłania na kanale #multiplayer-help-requests na oficjalnym serwerze Discord gry simrail. Nie zapomnij dodać powodu zgłoszenia!"
},
"buttons": {
"report": "Zgłoś",
"copy": "Kopiuj link"
} }
} },
"sidebar": {
"home": "Strona główna",
"logs": "Logi",
"stations": "Stacje",
"trains": "Pociągi",
"leaderboard": "Tablica wyników",
"info": "INFO",
"admin": "ADMIN"
},
"admin": {}
} }

View File

@ -1,9 +1,8 @@
import React, { useState, ReactNode } from "react"; import React, { useState, ReactNode } from 'react';
import Header from "../components/mini/header/index"; import { Header } from '../components/mini/header/Header';
import Sidebar from "../components/mini/sidebar/index"; import { Sidebar } from '../components/mini/sidebar/Sidebar';
const DefaultLayout: React.FC<{ children: ReactNode }> = ({ children }) => export const DefaultLayout: React.FC<{ children: ReactNode }> = ({ children }) => {
{
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
return ( return (

View File

@ -1,23 +1,21 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from 'react';
import CardDataStats from "../old/CardDataStats"; import CardDataStats from '../old/CardDataStats';
import { useTranslation, Trans } from "react-i18next"; import { useTranslation, Trans } from 'react-i18next';
import { Link } from "react-router-dom"; import { Link } from 'react-router-dom';
import { TStatsResponse } from "../types/stats.ts"; import { TStatsResponse } from '../types/stats.ts';
import { WarningAlert } from '../components/mini/alerts/Warning.tsx';
export const Home: React.FC = () => export const Home: React.FC = () => {
{
const { t } = useTranslation(); const { t } = useTranslation();
const [ commit, setCommit ] = useState(""); const [commit, setCommit] = useState('');
const [ version, setVersion ] = useState(""); const [version, setVersion] = useState('');
const [trains, setTrains] = useState(0); const [trains, setTrains] = useState(0);
const [dispatchers, setDispatchers] = useState(0); const [dispatchers, setDispatchers] = useState(0);
const [profiles, setProfiles] = useState(0); const [profiles, setProfiles] = useState(0);
useEffect(() => useEffect(() => {
{ fetch(`${import.meta.env.VITE_API_URL}/stats/`).then(x => x.json()).then((data: TStatsResponse) => {
fetch(`${ import.meta.env.VITE_API_URL }/stats/`).then(x => x.json()).then((data: TStatsResponse) =>
{
data.data.git.commit && setCommit(data.data.git.commit); data.data.git.commit && setCommit(data.data.git.commit);
data.data.git.version && setVersion(data.data.git.version); data.data.git.version && setVersion(data.data.git.version);
@ -30,11 +28,15 @@ export const Home: React.FC = () =>
return ( return (
<> <>
<div className="flex pb-5">
<WarningAlert description={t('preview.description')} title={t('preview.title')} />
</div>
<div className="flex flex-col gap-10"> <div className="flex flex-col gap-10">
<div className="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-6 xl:grid-cols-3 2xl:gap-7.5"> <div className="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-6 xl:grid-cols-3 2xl:gap-7.5">
<CardDataStats title={ t("home.stats.trains") } total={ trains.toString() }/> <CardDataStats title={t('home.stats.trains')} total={trains.toString()} />
<CardDataStats title={ t("home.stats.dispatchers") } total={ dispatchers.toString() }/> <CardDataStats title={t('home.stats.dispatchers')} total={dispatchers.toString()} />
<CardDataStats title={ t("home.stats.profiles") } total={ profiles.toString() }/> <CardDataStats title={t('home.stats.profiles')} total={profiles.toString()} />
</div> </div>
@ -43,22 +45,22 @@ export const Home: React.FC = () =>
<div className="px-4 pb-6 text-center"> <div className="px-4 pb-6 text-center">
<div className="mt-4"> <div className="mt-4">
<h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white"> <h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white">
{ t("home.title") } {t('home.title')}
</h3> </h3>
<p className="font-medium">{ t("home.description") }</p> <p className="font-medium">{t('home.description')}</p>
<div className="p-4 md:p-6 xl:p-9 flex gap-2 justify-center"> <div className="p-4 md:p-6 xl:p-9 flex gap-2 justify-center">
<Link <Link
to="https://git.alekswilc.dev/simrail/simrail.alekswilc.dev" to="https://git.alekswilc.dev/simrail/simrail.alekswilc.dev"
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
> >
{ t("home.buttons.project") } {t('home.buttons.project')}
</Link> </Link>
<Link <Link
to="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/" to="https://forum.simrail.eu/topic/9142-logowanie-wyj%C5%9B%C4%87-z-posterunk%C3%B3w/"
className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10" className="inline-flex items-center justify-center rounded-md bg-primary py-2 px-8 text-center font-medium text-white hover:bg-opacity-90 lg:px-8 xl:px-10"
> >
{ t("home.buttons.forum") } {t('home.buttons.forum')}
</Link> </Link>
</div> </div>
</div> </div>
@ -71,28 +73,28 @@ export const Home: React.FC = () =>
<div className="mt-6.5"> <div className="mt-6.5">
<p><Trans <p><Trans
i18nKey={ t("home.footer.author") } i18nKey={t('home.footer.author')}
values={ { author: "alekswilc" } } values={{ author: 'alekswilc' }}
components={{ components={{
anchor: <Link className="color-orchid" to={ "https://www.alekswilc.dev" }/>, anchor: <Link className="color-orchid" to={'https://www.alekswilc.dev'} />
}} }}
/></p> /></p>
<p><Trans <p><Trans
i18nKey={ t("home.footer.thanks") } i18nKey={t('home.footer.thanks')}
components={{ components={{
bahu: <Link className="color-orchid" to={ "https://bahu.pro/" }/>, bahu: <Link className="color-orchid" to={'https://bahu.pro/'} />,
simrailelite: <Link className="color-orchid" simrailelite: <Link className="color-orchid"
to={ "https://discord.gg/yDhy3pDrVr" }/>, to={'https://discord.gg/yDhy3pDrVr'} />
}} }}
/></p> /></p>
<p>{ t("home.footer.license") } <Link className="color-orchid" <p>{t('home.footer.license')} <Link className="color-orchid"
to={ "https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/src/branch/main/LICENSE" }>GNU to={'https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/src/branch/main/LICENSE'}>GNU
AGPL V3</Link></p> AGPL V3</Link></p>
<p>{ t("home.footer.powered") } <Link className="color-orchid" <p>{t('home.footer.powered')} <Link className="color-orchid"
to={ "https://tailadmin.com/" }>TailAdmin</Link></p> to={'https://tailadmin.com/'}>TailAdmin</Link></p>
<p>{version && <Link className="color-orchid" <p>{version && <Link className="color-orchid"
to={ `https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/releases/tag/${ version }` }>{ version }</Link> }{ version && commit && " | " }{ commit && to={`https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/releases/tag/${version}`}>{version}</Link>}{version && commit && ' | '}{commit &&
<Link className="color-orchid" <Link className="color-orchid"
to={`https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/commit/${commit}`}>{commit}</Link>}</p> to={`https://git.alekswilc.dev/simrail/simrail.alekswilc.dev/commit/${commit}`}>{commit}</Link>}</p>

View File

@ -1,28 +1,23 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from 'react';
import { useParams } from "react-router-dom"; import { useParams } from 'react-router-dom';
import { ContentLoader } from "../../components/mini/loaders/ContentLoader.tsx"; import { ContentLoader } from '../../components/mini/loaders/ContentLoader.tsx';
import { WarningAlert } from "../../components/mini/alerts/Warning.tsx"; import { WarningAlert } from '../../components/mini/alerts/Warning.tsx';
import { useTranslation } from "react-i18next"; import { useTranslation } from 'react-i18next';
import { PageTitle } from "../../components/mini/util/PageTitle.tsx"; import { PageTitle } from '../../components/mini/util/PageTitle.tsx';
import { TLogResponse, TLogStationData, TLogTrainData } from "../../types/log.ts"; import { TLogResponse, TLogStationData, TLogTrainData } from '../../types/log.ts';
import { StationLog } from "../../components/pages/log/StationLog.tsx"; import { StationLog } from '../../components/pages/log/StationLog.tsx';
import { TrainLog } from '../../components/pages/log/TrainLog.tsx';
export const Log = () => export const Log = () => {
{
const { id } = useParams(); const { id } = useParams();
const [error, setError] = useState<0 | 1 | 2 | 3>(0); const [error, setError] = useState<0 | 1 | 2 | 3>(0);
const [trainData, setTrainData] = useState<TLogTrainData>(undefined!); const [trainData, setTrainData] = useState<TLogTrainData>(undefined!);
const [stationData, setStationData] = useState<TLogStationData>(undefined!); const [stationData, setStationData] = useState<TLogStationData>(undefined!);
trainData; // suppress error useEffect(() => {
fetch(`${import.meta.env.VITE_API_URL}/log/${id}`).then(x => x.json()).then((data: TLogResponse) => {
useEffect(() => switch (data.code) {
{
fetch(`${ import.meta.env.VITE_API_URL }/log/${ id }`).then(x => x.json()).then((data: TLogResponse) =>
{
switch (data.code)
{
case 404: case 404:
setError(2); setError(2);
break; break;
@ -33,7 +28,7 @@ export const Log = () =>
case 200: case 200:
setError(1); setError(1);
"trainNumber" in data.data ? setTrainData(data.data) : setStationData(data.data); 'trainNumber' in data.data ? setTrainData(data.data) : setStationData(data.data);
break; break;
} }
}); });
@ -47,16 +42,20 @@ export const Log = () =>
{error === 0 && <ContentLoader />} {error === 0 && <ContentLoader />}
{/* NOT FOUND */} {/* NOT FOUND */}
{error === 2 && <PageTitle title={`simrail.alekswilc.dev | Profile not found`} />} {error === 2 && <PageTitle title={`simrail.alekswilc.dev | Profile not found`} />}
{ error === 2 && <WarningAlert title={ t("log.errors.notfound.title") } {error === 2 && <WarningAlert title={t('log.errors.notfound.title')}
description={ t("log.errors.notfound.description") }/> } description={t('log.errors.notfound.description')} />}
{/* BLACKLISTED PROFILE */} {/* BLACKLISTED PROFILE */}
{error === 3 && <PageTitle title={`simrail.alekswilc.dev | Blacklisted profile`} />} {error === 3 && <PageTitle title={`simrail.alekswilc.dev | Blacklisted profile`} />}
{ error === 3 && <WarningAlert title={ t("log.errors.blacklist.title") } {error === 3 && <WarningAlert title={t('log.errors.blacklist.title')}
description={ t("log.errors.blacklist.description") }/> } description={t('log.errors.blacklist.description')} />}
{/* SUCCESS */} {/* SUCCESS */}
{error === 1 && stationData && <PageTitle {error === 1 && stationData && <PageTitle
title={`simrail.alekswilc.dev | ${stationData.userUsername} | ${stationData.stationName}`} />} title={`simrail.alekswilc.dev | ${stationData.userUsername} | ${stationData.stationName}`} />}
{error === 1 && stationData && < StationLog data={stationData} />} {error === 1 && stationData && < StationLog data={stationData} />}
{error === 1 && trainData && <PageTitle
title={`simrail.alekswilc.dev | ${trainData.userUsername} | ${trainData.trainName} ${trainData.trainNumber}`} />}
{error === 1 && trainData && < TrainLog data={trainData} />}
</> </>
); );
}; };

View File

@ -13,8 +13,8 @@ export interface TLogTrainData
userUsername: string; userUsername: string;
userAvatar: string; userAvatar: string;
leftDate: number; leftDate: number;
distance: number; distance?: number;
points: number; points?: number;
server: string; server: string;
trainName: string; trainName: string;
joinedDate?: number; joinedDate?: number;