Development #25
+17
-8
@@ -1,5 +1,5 @@
|
|||||||
// Grant or revoke the "Admin" role for a user by phone number (US +1 assumed).
|
// Grant or revoke a role for a user by phone number (US +1 assumed).
|
||||||
// Usage: bun run scripts/make-admin.ts <phone> [--revoke]
|
// Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
import SuperTokens from "supertokens-node";
|
import SuperTokens from "supertokens-node";
|
||||||
import Session from "supertokens-node/recipe/session";
|
import Session from "supertokens-node/recipe/session";
|
||||||
@@ -26,10 +26,19 @@ SuperTokens.init({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const VALID_ROLES = ["Admin", "Developer"] as const;
|
||||||
|
|
||||||
const raw = process.argv[2];
|
const raw = process.argv[2];
|
||||||
const revoke = process.argv.includes("--revoke");
|
const revoke = process.argv.includes("--revoke");
|
||||||
|
const roleFlagIndex = process.argv.indexOf("--role");
|
||||||
|
const role = roleFlagIndex === -1 ? "Admin" : process.argv[roleFlagIndex + 1];
|
||||||
|
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
console.error("Usage: bun run scripts/make-admin.ts <phone> [--revoke]");
|
console.error("Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!VALID_ROLES.includes(role as (typeof VALID_ROLES)[number])) {
|
||||||
|
console.error(`Invalid role "${role}". Valid roles: ${VALID_ROLES.join(", ")}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,15 +69,15 @@ if (!user) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserRoles.createNewRoleOrAddPermissions("Admin", []);
|
await UserRoles.createNewRoleOrAddPermissions(role, []);
|
||||||
|
|
||||||
if (revoke) {
|
if (revoke) {
|
||||||
const res = await UserRoles.removeUserRole("public", user.id, "Admin");
|
const res = await UserRoles.removeUserRole("public", user.id, role);
|
||||||
console.log(`Removed Admin from ${matched} (user ${user.id}):`, res.status);
|
console.log(`Removed ${role} from ${matched} (user ${user.id}):`, res.status);
|
||||||
} else {
|
} else {
|
||||||
const res = await UserRoles.addRoleToUser("public", user.id, "Admin");
|
const res = await UserRoles.addRoleToUser("public", user.id, role);
|
||||||
console.log(
|
console.log(
|
||||||
`Granted Admin to ${matched} (user ${user.id}):`,
|
`Granted ${role} to ${matched} (user ${user.id}):`,
|
||||||
res.status === "OK"
|
res.status === "OK"
|
||||||
? res.didUserAlreadyHaveRole
|
? res.didUserAlreadyHaveRole
|
||||||
? "already had it"
|
? "already had it"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Outlet, createFileRoute, useNavigate, useRouterState } from "@tanstack/react-router";
|
import { Outlet, createFileRoute, redirect, useNavigate, useRouterState } from "@tanstack/react-router";
|
||||||
import { Tabs } from "@mantine/core";
|
import { Tabs } from "@mantine/core";
|
||||||
import { Trans } from "@lingui/react/macro";
|
import { Trans } from "@lingui/react/macro";
|
||||||
import { msg } from "@lingui/core/macro";
|
import { msg } from "@lingui/core/macro";
|
||||||
|
import { isDeveloperRole } from "@/features/core/utils/roles";
|
||||||
|
|
||||||
const TAB_ROUTES = {
|
const TAB_ROUTES = {
|
||||||
usage: "/admin/telemetry/usage",
|
usage: "/admin/telemetry/usage",
|
||||||
@@ -15,6 +16,11 @@ type TabValue = keyof typeof TAB_ROUTES;
|
|||||||
|
|
||||||
export const Route = createFileRoute("/_authed/admin/telemetry")({
|
export const Route = createFileRoute("/_authed/admin/telemetry")({
|
||||||
component: TelemetryLayout,
|
component: TelemetryLayout,
|
||||||
|
beforeLoad: ({ context }) => {
|
||||||
|
if (!isDeveloperRole(context.auth?.roles)) {
|
||||||
|
throw redirect({ to: "/admin" });
|
||||||
|
}
|
||||||
|
},
|
||||||
loader: () => ({
|
loader: () => ({
|
||||||
header: {
|
header: {
|
||||||
title: msg`Telemetry`,
|
title: msg`Telemetry`,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { msg } from "@lingui/core/macro";
|
import { msg } from "@lingui/core/macro";
|
||||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||||
|
import { isAdminRole } from "@/features/core/utils/roles";
|
||||||
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({
|
|||||||
const i18n = localizedFor(context.metadata);
|
const i18n = localizedFor(context.metadata);
|
||||||
try {
|
try {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
|
|
||||||
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { msg } from "@lingui/core/macro";
|
import { msg } from "@lingui/core/macro";
|
||||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||||
|
import { isAdminRole } from "@/features/core/utils/roles";
|
||||||
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({
|
|||||||
const i18n = localizedFor(context.metadata);
|
const i18n = localizedFor(context.metadata);
|
||||||
try {
|
try {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
|
|
||||||
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||||
if (!isAdmin) return new Response("Unauthorized", { status: 403 });
|
if (!isAdmin) return new Response("Unauthorized", { status: 403 });
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Box } from "@mantine/core";
|
||||||
|
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
||||||
|
|
||||||
|
const HeadToHeadIcon = ({ size = 14 }: { size?: number }) => (
|
||||||
|
<Box
|
||||||
|
component="span"
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
display: "inline-block",
|
||||||
|
width: size * (27.5 / 14),
|
||||||
|
height: size * (16 / 14),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FootballHelmetIcon
|
||||||
|
size={size}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
transform: "rotate(25deg)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FootballHelmetIcon
|
||||||
|
size={size}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
transform: "scaleX(-1) rotate(25deg)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default HeadToHeadIcon;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -17,7 +17,7 @@ const activitySearchParamsSchema = z.object({
|
|||||||
|
|
||||||
export const searchActivities = createServerFn()
|
export const searchActivities = createServerFn()
|
||||||
.validator(activitySearchParamsSchema)
|
.validator(activitySearchParamsSchema)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<ActivityListResult>(async () => {
|
toServerResult<ActivityListResult>(async () => {
|
||||||
const result = await pbAdmin.searchActivities(data);
|
const result = await pbAdmin.searchActivities(data);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { List, Text } from "@mantine/core";
|
import { List, Text } from "@mantine/core";
|
||||||
import { Plural, Trans, useLingui } from "@lingui/react/macro";
|
import { Plural, Trans, useLingui } from "@lingui/react/macro";
|
||||||
import {
|
import {
|
||||||
DatabaseIcon,
|
|
||||||
TreeStructureIcon,
|
TreeStructureIcon,
|
||||||
TrophyIcon,
|
TrophyIcon,
|
||||||
MedalIcon,
|
MedalIcon,
|
||||||
@@ -10,12 +9,14 @@ import {
|
|||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
import ListLink from "@/components/list-link";
|
import ListLink from "@/components/list-link";
|
||||||
import ListButton from "@/components/list-button";
|
import ListButton from "@/components/list-button";
|
||||||
|
import { useIsDeveloper } from "@/features/core/hooks/use-is-developer";
|
||||||
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
|
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
|
||||||
import { useServerQuery } from "@/lib/tanstack-query/hooks";
|
import { useServerQuery } from "@/lib/tanstack-query/hooks";
|
||||||
import toast from "@/lib/sonner";
|
import toast from "@/lib/sonner";
|
||||||
|
|
||||||
export const ToolsList = () => {
|
export const ToolsList = () => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
const isDeveloper = useIsDeveloper();
|
||||||
|
|
||||||
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
|
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
|
||||||
queryKey: ["admin", "badge-migration-status"],
|
queryKey: ["admin", "badge-migration-status"],
|
||||||
@@ -51,11 +52,13 @@ export const ToolsList = () => {
|
|||||||
Icon={TrophyIcon}
|
Icon={TrophyIcon}
|
||||||
to="/admin/tournaments"
|
to="/admin/tournaments"
|
||||||
/>
|
/>
|
||||||
<ListLink
|
{isDeveloper && (
|
||||||
label={t`Telemetry`}
|
<ListLink
|
||||||
Icon={PulseIcon}
|
label={t`Telemetry`}
|
||||||
to="/admin/telemetry/usage"
|
Icon={PulseIcon}
|
||||||
/>
|
to="/admin/telemetry/usage"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<ListLink
|
<ListLink
|
||||||
label={t`Award Badges`}
|
label={t`Award Badges`}
|
||||||
Icon={CrownIcon}
|
Icon={CrownIcon}
|
||||||
@@ -91,13 +94,6 @@ export const ToolsList = () => {
|
|||||||
<Trans>Badge recalculation failed. Check the server logs.</Trans>
|
<Trans>Badge recalculation failed. Check the server logs.</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<ListButton
|
|
||||||
label={t`Open Pocketbase`}
|
|
||||||
Icon={DatabaseIcon}
|
|
||||||
onClick={() =>
|
|
||||||
window.location.replace(process.env.POCKETBASE_URL! + "/_/")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListLink
|
<ListLink
|
||||||
label={t`Bracket Preview`}
|
label={t`Bracket Preview`}
|
||||||
Icon={TreeStructureIcon}
|
Icon={TreeStructureIcon}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
||||||
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { Match } from "@/features/matches/types";
|
import { Match } from "@/features/matches/types";
|
||||||
import TeamAvatar from "@/components/team-avatar";
|
import TeamAvatar from "@/components/team-avatar";
|
||||||
@@ -188,28 +188,7 @@ const MatchDock = ({ match, onClose }: MatchDockProps) => {
|
|||||||
w={40}
|
w={40}
|
||||||
style={{ flexShrink: 0 }}
|
style={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
<Group
|
<HeadToHeadIcon />
|
||||||
style={{ position: "relative", width: 27.5, height: 16 }}
|
|
||||||
>
|
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: "rotate(25deg)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: "scaleX(-1) rotate(25deg)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { useAuth } from "@/contexts/auth-context";
|
||||||
|
import { isDeveloperRole } from "../utils/roles";
|
||||||
|
|
||||||
|
export const useIsDeveloper = (): boolean => {
|
||||||
|
const { roles } = useAuth();
|
||||||
|
return isDeveloperRole(roles);
|
||||||
|
};
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
export const ADMIN_ROLE = "Admin";
|
export const ADMIN_ROLE = "Admin";
|
||||||
|
export const DEVELOPER_ROLE = "Developer";
|
||||||
|
|
||||||
|
export const isDeveloperRole = (roles?: string[] | null): boolean =>
|
||||||
|
!!roles?.includes(DEVELOPER_ROLE);
|
||||||
|
|
||||||
export const isAdminRole = (roles?: string[] | null): boolean =>
|
export const isAdminRole = (roles?: string[] | null): boolean =>
|
||||||
!!roles?.includes(ADMIN_ROLE);
|
!!roles?.includes(ADMIN_ROLE) || isDeveloperRole(roles);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Text, Group, Stack, Paper, Indicator, Box, Tooltip, ActionIcon } from "@mantine/core";
|
import { Text, Group, Stack, Paper, Indicator, Box, Tooltip, ActionIcon } from "@mantine/core";
|
||||||
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
import { Match } from "../types";
|
import { Match } from "../types";
|
||||||
import TeamAvatar from "@/components/team-avatar";
|
import TeamAvatar from "@/components/team-avatar";
|
||||||
@@ -94,26 +94,7 @@ const MatchCard = ({ match, hideH2H = false }: MatchCardProps) => {
|
|||||||
aria-label={t`View head-to-head`}
|
aria-label={t`View head-to-head`}
|
||||||
w={40}
|
w={40}
|
||||||
>
|
>
|
||||||
<Group style={{ position: 'relative', width: 27.5, height: 16 }}>
|
<HeadToHeadIcon />
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: 'rotate(25deg)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: 'scaleX(-1) rotate(25deg)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import Sheet from "@/components/sheet/sheet";
|
import Sheet from "@/components/sheet/sheet";
|
||||||
import { useAuth } from "@/contexts/auth-context";
|
import { useAuth } from "@/contexts/auth-context";
|
||||||
import { Flex, Title, ActionIcon, Stack, Button, Box } from "@mantine/core";
|
import { Flex, Title, ActionIcon, Stack, Button } from "@mantine/core";
|
||||||
import { PencilIcon, FootballHelmetIcon } from "@phosphor-icons/react";
|
import { PencilIcon } from "@phosphor-icons/react";
|
||||||
|
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useLingui } from "@lingui/react/macro";
|
import { useLingui } from "@lingui/react/macro";
|
||||||
import NameUpdateForm from "./name-form";
|
import NameUpdateForm from "./name-form";
|
||||||
@@ -64,26 +65,7 @@ const Header = ({ player }: HeaderProps) => {
|
|||||||
w={40}
|
w={40}
|
||||||
display={!owner ? 'block' : 'none'}
|
display={!owner ? 'block' : 'none'}
|
||||||
>
|
>
|
||||||
<Box style={{ position: 'relative', width: 27.5, height: 16 }}>
|
<HeadToHeadIcon />
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: 'rotate(25deg)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<FootballHelmetIcon
|
|
||||||
size={14}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
transform: 'scaleX(-1) rotate(25deg)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { CaretLeftIcon, CaretRightIcon, InfoIcon } from "@phosphor-icons/react";
|
import { CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
|
||||||
|
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||||
import WizardOrbIcon from "@/components/wizard-orb-icon";
|
import WizardOrbIcon from "@/components/wizard-orb-icon";
|
||||||
import { Trans, useLingui } from "@lingui/react/macro";
|
import { Trans, useLingui } from "@lingui/react/macro";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
@@ -228,7 +229,7 @@ export const PredictionEditor: React.FC<PredictionEditorProps> = ({
|
|||||||
disabled={!activeResolved?.home && !activeResolved?.away}
|
disabled={!activeResolved?.home && !activeResolved?.away}
|
||||||
aria-label={t`Matchup details`}
|
aria-label={t`Matchup details`}
|
||||||
>
|
>
|
||||||
<InfoIcon size={16} />
|
<HeadToHeadIcon />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Group>
|
</Group>
|
||||||
<Group gap="sm" align="center" wrap="nowrap">
|
<Group gap="sm" align="center" wrap="nowrap">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core";
|
|||||||
import { Trans, useLingui } from "@lingui/react/macro";
|
import { Trans, useLingui } from "@lingui/react/macro";
|
||||||
import { useWebPush } from "@/hooks/use-web-push";
|
import { useWebPush } from "@/hooks/use-web-push";
|
||||||
import { useAuth } from "@/contexts/auth-context";
|
import { useAuth } from "@/contexts/auth-context";
|
||||||
import { useIsAdmin } from "@/features/core/hooks/use-is-admin";
|
import { useIsDeveloper } from "@/features/core/hooks/use-is-developer";
|
||||||
import { updateUserTelemetryAlerts } from "@/features/settings/server";
|
import { updateUserTelemetryAlerts } from "@/features/settings/server";
|
||||||
import toast from "@/lib/sonner";
|
import toast from "@/lib/sonner";
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ const TelemetryAlertsSwitch = () => {
|
|||||||
export function NotificationsSection() {
|
export function NotificationsSection() {
|
||||||
const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } =
|
const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } =
|
||||||
useWebPush();
|
useWebPush();
|
||||||
const isAdmin = useIsAdmin();
|
const isDeveloper = useIsDeveloper();
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
if (!supported || !configured) {
|
if (!supported || !configured) {
|
||||||
@@ -99,7 +99,7 @@ export function NotificationsSection() {
|
|||||||
disabled={busy}
|
disabled={busy}
|
||||||
label={t`Enable notifications on this device`}
|
label={t`Enable notifications on this device`}
|
||||||
/>
|
/>
|
||||||
{subscribed && isAdmin && <TelemetryAlertsSwitch />}
|
{subscribed && isDeveloper && <TelemetryAlertsSwitch />}
|
||||||
{subscribed && (
|
{subscribed && (
|
||||||
<Group>
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createServerFn } from "@tanstack/react-start";
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
import {
|
import {
|
||||||
superTokensAdminFunctionMiddleware,
|
superTokensDeveloperFunctionMiddleware,
|
||||||
superTokensFunctionMiddleware,
|
superTokensFunctionMiddleware,
|
||||||
} from "@/utils/supertokens";
|
} from "@/utils/supertokens";
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ export const updateUserAccentColor = createServerFn({ method: "POST" })
|
|||||||
|
|
||||||
export const updateUserTelemetryAlerts = createServerFn({ method: "POST" })
|
export const updateUserTelemetryAlerts = createServerFn({ method: "POST" })
|
||||||
.validator((data: boolean) => data === true)
|
.validator((data: boolean) => data === true)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ context, data }) => {
|
.handler(async ({ context, data }) => {
|
||||||
const { userAuthId, metadata } = context;
|
const { userAuthId, metadata } = context;
|
||||||
if (!userAuthId) return;
|
if (!userAuthId) return;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { superTokensFunctionMiddleware, superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
import { superTokensFunctionMiddleware, superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
||||||
|
import { isAdminRole } from "@/features/core/utils/roles";
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -35,8 +36,8 @@ export const createTeam = createServerFn()
|
|||||||
.handler(async ({ data, context }) =>
|
.handler(async ({ data, context }) =>
|
||||||
toServerResult(async () => {
|
toServerResult(async () => {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
|
|
||||||
//if (!isAdmin && !data.players.includes(userId)) {
|
//if (!isAdmin && !data.players.includes(userId)) {
|
||||||
// throw new Error("You can only create teams that include yourself as a player");
|
// throw new Error("You can only create teams that include yourself as a player");
|
||||||
//}
|
//}
|
||||||
@@ -57,7 +58,7 @@ export const updateTeam = createServerFn()
|
|||||||
.handler(async ({ data: { id, updates }, context }) =>
|
.handler(async ({ data: { id, updates }, context }) =>
|
||||||
toServerResult(async () => {
|
toServerResult(async () => {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
|
|
||||||
const team = await pbAdmin.getTeam(id);
|
const team = await pbAdmin.getTeam(id);
|
||||||
if (!team) {
|
if (!team) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createServerFn } from "@tanstack/react-start";
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||||
import {
|
import {
|
||||||
@@ -37,7 +37,7 @@ const clientEventSearchSchema = z.object({
|
|||||||
|
|
||||||
export const searchClientEvents = createServerFn()
|
export const searchClientEvents = createServerFn()
|
||||||
.validator(clientEventSearchSchema)
|
.validator(clientEventSearchSchema)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<TelemetryListResult<ClientEvent>>(async () => {
|
toServerResult<TelemetryListResult<ClientEvent>>(async () => {
|
||||||
const result = await pbAdmin.searchClientEvents(data);
|
const result = await pbAdmin.searchClientEvents(data);
|
||||||
@@ -58,7 +58,7 @@ const clientErrorSearchSchema = z.object({
|
|||||||
|
|
||||||
export const searchClientErrors = createServerFn()
|
export const searchClientErrors = createServerFn()
|
||||||
.validator(clientErrorSearchSchema)
|
.validator(clientErrorSearchSchema)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<TelemetryListResult<ClientError>>(async () => {
|
toServerResult<TelemetryListResult<ClientError>>(async () => {
|
||||||
const result = await pbAdmin.searchClientErrors(data);
|
const result = await pbAdmin.searchClientErrors(data);
|
||||||
@@ -77,7 +77,7 @@ const errorGroupsSchema = z.object({
|
|||||||
// not on the next rollup tick.
|
// not on the next rollup tick.
|
||||||
export const getClientErrorGroups = createServerFn()
|
export const getClientErrorGroups = createServerFn()
|
||||||
.validator(errorGroupsSchema)
|
.validator(errorGroupsSchema)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<ClientErrorGroup[]>(async () => {
|
toServerResult<ClientErrorGroup[]>(async () => {
|
||||||
const { from, to, includeResolved = false, limit = 50 } = data;
|
const { from, to, includeResolved = false, limit = 50 } = data;
|
||||||
@@ -141,7 +141,7 @@ export const getClientErrorGroups = createServerFn()
|
|||||||
|
|
||||||
export const resolveClientErrorGroup = createServerFn({ method: "POST" })
|
export const resolveClientErrorGroup = createServerFn({ method: "POST" })
|
||||||
.validator(z.object({ groupHash: z.string(), resolved: z.boolean().optional() }))
|
.validator(z.object({ groupHash: z.string(), resolved: z.boolean().optional() }))
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<{ updated: number }>(async () => {
|
toServerResult<{ updated: number }>(async () => {
|
||||||
const updated = await pbAdmin.resolveErrorsByGroup(data.groupHash, data.resolved ?? true);
|
const updated = await pbAdmin.resolveErrorsByGroup(data.groupHash, data.resolved ?? true);
|
||||||
@@ -158,7 +158,7 @@ const rollupRangeSchema = z.object({
|
|||||||
|
|
||||||
export const getRollupRange = createServerFn()
|
export const getRollupRange = createServerFn()
|
||||||
.validator(rollupRangeSchema)
|
.validator(rollupRangeSchema)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<RollupRow[]>(async () => pbAdmin.getRollupRange(data))
|
toServerResult<RollupRow[]>(async () => pbAdmin.getRollupRange(data))
|
||||||
);
|
);
|
||||||
@@ -167,7 +167,7 @@ export const listTelemetryAlerts = createServerFn()
|
|||||||
.validator(
|
.validator(
|
||||||
z.object({ page: z.number().optional(), perPage: z.number().max(200).optional() })
|
z.object({ page: z.number().optional(), perPage: z.number().max(200).optional() })
|
||||||
)
|
)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<TelemetryListResult<TelemetryAlert>>(async () =>
|
toServerResult<TelemetryListResult<TelemetryAlert>>(async () =>
|
||||||
pbAdmin.listTelemetryAlerts(data.page, data.perPage)
|
pbAdmin.listTelemetryAlerts(data.page, data.perPage)
|
||||||
@@ -197,7 +197,7 @@ export const listSessions = createServerFn()
|
|||||||
perPage: z.number().max(100).optional(),
|
perPage: z.number().max(100).optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<TelemetryListResult<SessionSummary>>(async () => {
|
toServerResult<TelemetryListResult<SessionSummary>>(async () => {
|
||||||
const { days = 7, page = 1, perPage = 30 } = data;
|
const { days = 7, page = 1, perPage = 30 } = data;
|
||||||
@@ -285,7 +285,7 @@ const MAX_TIMELINE_ENTRIES = 500;
|
|||||||
|
|
||||||
export const getSessionTimeline = createServerFn()
|
export const getSessionTimeline = createServerFn()
|
||||||
.validator(z.object({ sessionId: z.string().min(8).max(64) }))
|
.validator(z.object({ sessionId: z.string().min(8).max(64) }))
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async ({ data }) =>
|
.handler(async ({ data }) =>
|
||||||
toServerResult<SessionTimeline>(async () => {
|
toServerResult<SessionTimeline>(async () => {
|
||||||
const events = await pbAdmin.searchClientEvents({
|
const events = await pbAdmin.searchClientEvents({
|
||||||
@@ -426,7 +426,7 @@ export const getSessionTimeline = createServerFn()
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getTelemetryRuntimeStatus = createServerFn()
|
export const getTelemetryRuntimeStatus = createServerFn()
|
||||||
.middleware([superTokensAdminFunctionMiddleware])
|
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||||
.handler(async () =>
|
.handler(async () =>
|
||||||
toServerResult<TelemetryRuntimeStatus>(async () => {
|
toServerResult<TelemetryRuntimeStatus>(async () => {
|
||||||
ensureTelemetryScheduler();
|
ensureTelemetryScheduler();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens";
|
import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens";
|
||||||
|
import { isAdminRole } from "@/features/core/utils/roles";
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
import { createServerFn } from "@tanstack/react-start";
|
||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { tournamentInputSchema, GroupStanding } from "@/features/tournaments/types";
|
import { tournamentInputSchema, GroupStanding } from "@/features/tournaments/types";
|
||||||
@@ -46,7 +47,7 @@ export const getTournament = createServerFn()
|
|||||||
.validator(z.string())
|
.validator(z.string())
|
||||||
.middleware([superTokensFunctionMiddleware])
|
.middleware([superTokensFunctionMiddleware])
|
||||||
.handler(async ({ data: tournamentId, context }) => {
|
.handler(async ({ data: tournamentId, context }) => {
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
return toServerResult(() => pbAdmin.getTournament(tournamentId, isAdmin));
|
return toServerResult(() => pbAdmin.getTournament(tournamentId, isAdmin));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ export const enrollTeam = createServerFn()
|
|||||||
.handler(async ({ data: { tournamentId, teamId }, context }) =>
|
.handler(async ({ data: { tournamentId, teamId }, context }) =>
|
||||||
toServerResult(async () => {
|
toServerResult(async () => {
|
||||||
const userId = context.userAuthId;
|
const userId = context.userAuthId;
|
||||||
const isAdmin = context.roles.includes("Admin");
|
const isAdmin = isAdminRole(context.roles);
|
||||||
|
|
||||||
const team = await pbAdmin.getTeam(teamId);
|
const team = await pbAdmin.getTeam(teamId);
|
||||||
if (!team) { throw new Error('Team not found'); }
|
if (!team) { throw new Error('Team not found'); }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { msg } from "@lingui/core/macro";
|
|||||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||||
import { sendPushToPlayer } from "@/lib/push";
|
import { sendPushToPlayer } from "@/lib/push";
|
||||||
import { Logger } from "@/lib/logger";
|
import { Logger } from "@/lib/logger";
|
||||||
import { ADMIN_ROLE } from "@/features/core/utils/roles";
|
import { DEVELOPER_ROLE } from "@/features/core/utils/roles";
|
||||||
|
|
||||||
const logger = new Logger("Telemetry > Alerts");
|
const logger = new Logger("Telemetry > Alerts");
|
||||||
|
|
||||||
@@ -22,23 +22,23 @@ const fnFailureThreshold = (): number => {
|
|||||||
|
|
||||||
const pbTimestamp = (date: Date): string => date.toISOString().replace("T", " ");
|
const pbTimestamp = (date: Date): string => date.toISOString().replace("T", " ");
|
||||||
|
|
||||||
const ADMIN_CACHE_TTL_MS = 10 * 60 * 1000;
|
const DEVELOPER_CACHE_TTL_MS = 10 * 60 * 1000;
|
||||||
let adminPlayersCache: { playerIds: string[]; expiresAt: number } | null = null;
|
let developerPlayersCache: { playerIds: string[]; expiresAt: number } | null = null;
|
||||||
|
|
||||||
const getAdminPlayerIds = async (): Promise<string[]> => {
|
const getDeveloperPlayerIds = async (): Promise<string[]> => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (adminPlayersCache && adminPlayersCache.expiresAt > now) {
|
if (developerPlayersCache && developerPlayersCache.expiresAt > now) {
|
||||||
return adminPlayersCache.playerIds;
|
return developerPlayersCache.playerIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserRoles = (await import("supertokens-node/recipe/userroles")).default;
|
const UserRoles = (await import("supertokens-node/recipe/userroles")).default;
|
||||||
const UserMetadata = (await import("supertokens-node/recipe/usermetadata")).default;
|
const UserMetadata = (await import("supertokens-node/recipe/usermetadata")).default;
|
||||||
const response = await UserRoles.getUsersThatHaveRole("public", ADMIN_ROLE);
|
const response = await UserRoles.getUsersThatHaveRole("public", DEVELOPER_ROLE);
|
||||||
const users = response.status === "OK" ? response.users : [];
|
const users = response.status === "OK" ? response.users : [];
|
||||||
const playerIds: string[] = [];
|
const playerIds: string[] = [];
|
||||||
for (const userId of users) {
|
for (const userId of users) {
|
||||||
try {
|
try {
|
||||||
// Opt-in: only admins who enabled telemetry alerts in settings.
|
// Opt-in: only developers who enabled telemetry alerts in settings.
|
||||||
const { metadata } = await UserMetadata.getUserMetadata(userId);
|
const { metadata } = await UserMetadata.getUserMetadata(userId);
|
||||||
if (metadata?.telemetryAlerts !== true) continue;
|
if (metadata?.telemetryAlerts !== true) continue;
|
||||||
const player = await pbAdmin.getPlayerByAuthId(userId);
|
const player = await pbAdmin.getPlayerByAuthId(userId);
|
||||||
@@ -46,7 +46,7 @@ const getAdminPlayerIds = async (): Promise<string[]> => {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
adminPlayersCache = { playerIds, expiresAt: now + ADMIN_CACHE_TTL_MS };
|
developerPlayersCache = { playerIds, expiresAt: now + DEVELOPER_CACHE_TTL_MS };
|
||||||
return playerIds;
|
return playerIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,8 +71,8 @@ const fireAlert = async (candidate: AlertCandidate) => {
|
|||||||
value: candidate.value,
|
value: candidate.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
const admins = await getAdminPlayerIds();
|
const developers = await getDeveloperPlayerIds();
|
||||||
for (const playerId of admins) {
|
for (const playerId of developers) {
|
||||||
try {
|
try {
|
||||||
await sendPushToPlayer(playerId, {
|
await sendPushToPlayer(playerId, {
|
||||||
title: candidate.title,
|
title: candidate.title,
|
||||||
|
|||||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Ø Sieg-Marge"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Abzeichen verleihen"
|
msgstr "Abzeichen verleihen"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Abzeichen verleihen"
|
msgstr "Abzeichen verleihen"
|
||||||
|
|
||||||
@@ -606,15 +606,15 @@ msgstr "Zurück"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Abzeichen an {playerName} verliehen"
|
msgstr "Abzeichen an {playerName} verliehen"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:91
|
#: src/features/admin/components/dashboard/tools-list.tsx:94
|
||||||
msgid "Badge recalculation failed. Check the server logs."
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs."
|
msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs."
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
#: src/features/admin/components/dashboard/tools-list.tsx:40
|
||||||
msgid "Badge recalculation is already running"
|
msgid "Badge recalculation is already running"
|
||||||
msgstr "Abzeichen-Neuberechnung läuft bereits"
|
msgstr "Abzeichen-Neuberechnung läuft bereits"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:38
|
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
||||||
msgid "Badge recalculation started in the background"
|
msgid "Badge recalculation started in the background"
|
||||||
msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet"
|
msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet"
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
|||||||
msgstr "Turnierbaum"
|
msgstr "Turnierbaum"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:102
|
#: src/features/admin/components/dashboard/tools-list.tsx:98
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Turnierbaum-Vorschau"
|
msgstr "Turnierbaum-Vorschau"
|
||||||
|
|
||||||
@@ -1118,7 +1118,7 @@ msgstr "Fehlerspitzen und fehlschlagende Funktionen, als Push an dieses Konto"
|
|||||||
msgid "Error: {errorMessage}"
|
msgid "Error: {errorMessage}"
|
||||||
msgstr "Fehler: {errorMessage}"
|
msgstr "Fehler: {errorMessage}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
#: src/app/routes/_authed/admin/telemetry.tsx:63
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:260
|
#: src/features/telemetry/components/usage-screen.tsx:260
|
||||||
msgid "Errors"
|
msgid "Errors"
|
||||||
msgstr "Fehler"
|
msgstr "Fehler"
|
||||||
@@ -1186,7 +1186,7 @@ msgstr "Aktives Gerät konnte nicht festgelegt werden"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "Lautstärke konnte nicht festgelegt werden"
|
msgstr "Lautstärke konnte nicht festgelegt werden"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:42
|
#: src/features/admin/components/dashboard/tools-list.tsx:43
|
||||||
msgid "Failed to start badge recalculation"
|
msgid "Failed to start badge recalculation"
|
||||||
msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden"
|
msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden"
|
||||||
|
|
||||||
@@ -1198,8 +1198,8 @@ msgstr "Team konnte nicht aktualisiert werden"
|
|||||||
msgid "Failed to update tournament"
|
msgid "Failed to update tournament"
|
||||||
msgstr "Turnier konnte nicht aktualisiert werden"
|
msgstr "Turnier konnte nicht aktualisiert werden"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||||
#: src/features/teams/components/team-form/index.tsx:148
|
#: src/features/teams/components/team-form/index.tsx:148
|
||||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||||
msgid "Failed to upload logo"
|
msgid "Failed to upload logo"
|
||||||
@@ -1209,8 +1209,8 @@ msgstr "Logo konnte nicht hochgeladen werden"
|
|||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "Fehler"
|
msgstr "Fehler"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||||
msgid "File too large. Maximum size is 10MB."
|
msgid "File too large. Maximum size is 10MB."
|
||||||
msgstr "Datei zu groß. Maximale Größe ist 10 MB."
|
msgstr "Datei zu groß. Maximale Größe ist 10 MB."
|
||||||
|
|
||||||
@@ -1264,7 +1264,7 @@ msgstr "Funktion"
|
|||||||
msgid "Function Name"
|
msgid "Function Name"
|
||||||
msgstr "Funktionsname"
|
msgstr "Funktionsname"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||||
msgid "Functions"
|
msgid "Functions"
|
||||||
msgstr "Funktionen"
|
msgstr "Funktionen"
|
||||||
|
|
||||||
@@ -1335,11 +1335,11 @@ msgstr "Hast du schon mal mitgespielt?"
|
|||||||
|
|
||||||
#: src/app/routes/_authed/stats.tsx:56
|
#: src/app/routes/_authed/stats.tsx:56
|
||||||
#: src/features/bracket/components/match-dock.tsx:174
|
#: src/features/bracket/components/match-dock.tsx:174
|
||||||
#: src/features/bracket/components/match-dock.tsx:224
|
#: src/features/bracket/components/match-dock.tsx:203
|
||||||
#: src/features/matches/components/match-card.tsx:89
|
#: src/features/matches/components/match-card.tsx:89
|
||||||
#: src/features/matches/components/match-card.tsx:233
|
#: src/features/matches/components/match-card.tsx:214
|
||||||
#: src/features/players/components/league-head-to-head.tsx:198
|
#: src/features/players/components/league-head-to-head.tsx:198
|
||||||
#: src/features/players/components/profile/header.tsx:97
|
#: src/features/players/components/profile/header.tsx:79
|
||||||
msgid "Head to Head"
|
msgid "Head to Head"
|
||||||
msgstr "Direktvergleich"
|
msgstr "Direktvergleich"
|
||||||
|
|
||||||
@@ -1401,8 +1401,8 @@ msgstr "Falscher Code"
|
|||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "Ungültige Aktion"
|
msgstr "Ungültige Aktion"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||||
msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt."
|
msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt."
|
||||||
|
|
||||||
@@ -1410,8 +1410,8 @@ msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt."
|
|||||||
msgid "Invalid hex color format"
|
msgid "Invalid hex color format"
|
||||||
msgstr "Ungültiges Hex-Farbformat"
|
msgstr "Ungültiges Hex-Farbformat"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||||
msgid "Invalid input"
|
msgid "Invalid input"
|
||||||
msgstr "Ungültige Eingabe"
|
msgstr "Ungültige Eingabe"
|
||||||
|
|
||||||
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
|
|||||||
msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten"
|
msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten"
|
||||||
|
|
||||||
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:79
|
#: src/features/admin/components/dashboard/tools-list.tsx:82
|
||||||
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}"
|
msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}"
|
||||||
|
|
||||||
@@ -1504,8 +1504,8 @@ msgstr "Ort ist erforderlich"
|
|||||||
msgid "Logo"
|
msgid "Logo"
|
||||||
msgstr "Logo"
|
msgstr "Logo"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||||
msgid "Logo file is required"
|
msgid "Logo file is required"
|
||||||
msgstr "Logo-Datei ist erforderlich"
|
msgstr "Logo-Datei ist erforderlich"
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Team-Songs/Logos verwalten"
|
msgstr "Team-Songs/Logos verwalten"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:50
|
#: src/features/admin/components/dashboard/tools-list.tsx:51
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Turniere verwalten"
|
msgstr "Turniere verwalten"
|
||||||
|
|
||||||
@@ -1630,11 +1630,11 @@ msgstr "Matches für Regionals sind ungeordnet"
|
|||||||
msgid "Matches Played"
|
msgid "Matches Played"
|
||||||
msgstr "Gespielte Matches"
|
msgstr "Gespielte Matches"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||||
msgid "Matchup"
|
msgid "Matchup"
|
||||||
msgstr "Matchup"
|
msgstr "Matchup"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||||
msgid "Matchup details"
|
msgid "Matchup details"
|
||||||
msgstr "Matchup-Details"
|
msgstr "Matchup-Details"
|
||||||
|
|
||||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Ich"
|
msgstr "Ich"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Abzeichen-Fortschritt migrieren"
|
msgstr "Abzeichen-Fortschritt migrieren"
|
||||||
|
|
||||||
@@ -1712,7 +1712,7 @@ msgstr "Neuer Client-Fehler"
|
|||||||
msgid "New Player"
|
msgid "New Player"
|
||||||
msgstr "Neuer Spieler"
|
msgstr "Neuer Spieler"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||||
msgid "Next match"
|
msgid "Next match"
|
||||||
msgstr "Nächstes Match"
|
msgstr "Nächstes Match"
|
||||||
|
|
||||||
@@ -1908,10 +1908,6 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "Einmalcode"
|
msgstr "Einmalcode"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:95
|
|
||||||
msgid "Open Pocketbase"
|
|
||||||
msgstr "Pocketbase öffnen"
|
|
||||||
|
|
||||||
#: src/features/bracket/components/match-report.tsx:273
|
#: src/features/bracket/components/match-report.tsx:273
|
||||||
msgid "Opponent"
|
msgid "Opponent"
|
||||||
msgstr "Gegner"
|
msgstr "Gegner"
|
||||||
@@ -2047,7 +2043,7 @@ msgstr "Spieler erfolgreich entfernt"
|
|||||||
msgid "Player Stats"
|
msgid "Player Stats"
|
||||||
msgstr "Spielerstatistiken"
|
msgstr "Spielerstatistiken"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:48
|
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
||||||
#: src/features/teams/components/team-profile/index.tsx:29
|
#: src/features/teams/components/team-profile/index.tsx:29
|
||||||
msgid "Players"
|
msgid "Players"
|
||||||
msgstr "Spieler"
|
msgstr "Spieler"
|
||||||
@@ -2083,7 +2079,7 @@ msgstr "Tipps"
|
|||||||
msgid "Predictions are open"
|
msgid "Predictions are open"
|
||||||
msgstr "Tipps sind offen"
|
msgstr "Tipps sind offen"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||||
msgid "Previous match"
|
msgid "Previous match"
|
||||||
msgstr "Vorheriges Spiel"
|
msgstr "Vorheriges Spiel"
|
||||||
|
|
||||||
@@ -2122,7 +2118,7 @@ msgstr "Reaktionen"
|
|||||||
|
|
||||||
#. placeholder {0}: migrationStatus.playersProcessed
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
#. placeholder {1}: migrationStatus.totalPlayers
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:72
|
#: src/features/admin/components/dashboard/tools-list.tsx:75
|
||||||
msgid "Recalculating badges in the background ({0}/{1})"
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})"
|
msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})"
|
||||||
|
|
||||||
@@ -2366,7 +2362,7 @@ msgstr "Serverfunktionen"
|
|||||||
msgid "serverFn name"
|
msgid "serverFn name"
|
||||||
msgstr "serverFn-Name"
|
msgstr "serverFn-Name"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
#: src/app/routes/_authed/admin/telemetry.tsx:60
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:212
|
#: src/features/telemetry/components/usage-screen.tsx:212
|
||||||
msgid "Sessions"
|
msgid "Sessions"
|
||||||
msgstr "Sitzungen"
|
msgstr "Sitzungen"
|
||||||
@@ -2520,7 +2516,7 @@ msgstr "Status"
|
|||||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||||
#: src/features/login/components/player-prompt/index.tsx:115
|
#: src/features/login/components/player-prompt/index.tsx:115
|
||||||
#: src/features/login/components/player-prompt/index.tsx:126
|
#: src/features/login/components/player-prompt/index.tsx:126
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Absenden"
|
msgstr "Absenden"
|
||||||
@@ -2581,7 +2577,7 @@ msgstr "Team-Details"
|
|||||||
msgid "Team enrolled successfully!"
|
msgid "Team enrolled successfully!"
|
||||||
msgstr "Team erfolgreich angemeldet!"
|
msgstr "Team erfolgreich angemeldet!"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:93
|
#: src/app/routes/api/teams/upload-logo.ts:94
|
||||||
#: src/features/teams/components/team-profile/index.tsx:21
|
#: src/features/teams/components/team-profile/index.tsx:21
|
||||||
msgid "Team not found"
|
msgid "Team not found"
|
||||||
msgstr "Team nicht gefunden"
|
msgstr "Team nicht gefunden"
|
||||||
@@ -2615,8 +2611,8 @@ msgstr "Teams"
|
|||||||
msgid "Teams created successfully!"
|
msgid "Teams created successfully!"
|
||||||
msgstr "Teams erfolgreich erstellt!"
|
msgstr "Teams erfolgreich erstellt!"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||||
msgid "Telemetry"
|
msgid "Telemetry"
|
||||||
msgstr "Telemetrie"
|
msgstr "Telemetrie"
|
||||||
|
|
||||||
@@ -2730,7 +2726,7 @@ msgstr "Turnier erstellt, aber Logo-Upload fehlgeschlagen: {0}"
|
|||||||
msgid "Tournament created successfully!"
|
msgid "Tournament created successfully!"
|
||||||
msgstr "Turnier erfolgreich erstellt!"
|
msgstr "Turnier erfolgreich erstellt!"
|
||||||
|
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:94
|
#: src/app/routes/api/tournaments/upload-logo.ts:95
|
||||||
msgid "Tournament not found"
|
msgid "Tournament not found"
|
||||||
msgstr "Turnier nicht gefunden"
|
msgstr "Turnier nicht gefunden"
|
||||||
|
|
||||||
@@ -2784,8 +2780,8 @@ msgstr "Team abmelden"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "Unbekannter Fehler"
|
msgstr "Unbekannter Fehler"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||||
msgid "Unknown error occurred"
|
msgid "Unknown error occurred"
|
||||||
msgstr "Unbekannter Fehler aufgetreten"
|
msgstr "Unbekannter Fehler aufgetreten"
|
||||||
|
|
||||||
@@ -2810,7 +2806,7 @@ msgstr "Bevorstehend"
|
|||||||
msgid "Update Match"
|
msgid "Update Match"
|
||||||
msgstr "Match aktualisieren"
|
msgstr "Match aktualisieren"
|
||||||
|
|
||||||
#: src/features/players/components/profile/header.tsx:92
|
#: src/features/players/components/profile/header.tsx:74
|
||||||
msgid "Update Name"
|
msgid "Update Name"
|
||||||
msgstr "Namen aktualisieren"
|
msgstr "Namen aktualisieren"
|
||||||
|
|
||||||
@@ -2824,7 +2820,7 @@ msgstr "Team aktualisieren"
|
|||||||
msgid "Update Tournament"
|
msgid "Update Tournament"
|
||||||
msgstr "Turnier aktualisieren"
|
msgstr "Turnier aktualisieren"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||||
msgid "Usage"
|
msgid "Usage"
|
||||||
msgstr "Nutzung"
|
msgstr "Nutzung"
|
||||||
|
|
||||||
|
|||||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Avg Win Margin"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Award Badge"
|
msgstr "Award Badge"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Award Badges"
|
msgstr "Award Badges"
|
||||||
|
|
||||||
@@ -606,15 +606,15 @@ msgstr "Back"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Badge awarded to {playerName}"
|
msgstr "Badge awarded to {playerName}"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:91
|
#: src/features/admin/components/dashboard/tools-list.tsx:94
|
||||||
msgid "Badge recalculation failed. Check the server logs."
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
msgstr "Badge recalculation failed. Check the server logs."
|
msgstr "Badge recalculation failed. Check the server logs."
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
#: src/features/admin/components/dashboard/tools-list.tsx:40
|
||||||
msgid "Badge recalculation is already running"
|
msgid "Badge recalculation is already running"
|
||||||
msgstr "Badge recalculation is already running"
|
msgstr "Badge recalculation is already running"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:38
|
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
||||||
msgid "Badge recalculation started in the background"
|
msgid "Badge recalculation started in the background"
|
||||||
msgstr "Badge recalculation started in the background"
|
msgstr "Badge recalculation started in the background"
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
|||||||
msgstr "Bracket"
|
msgstr "Bracket"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:102
|
#: src/features/admin/components/dashboard/tools-list.tsx:98
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Bracket Preview"
|
msgstr "Bracket Preview"
|
||||||
|
|
||||||
@@ -1118,7 +1118,7 @@ msgstr "Error spikes and failing functions, pushed to this account"
|
|||||||
msgid "Error: {errorMessage}"
|
msgid "Error: {errorMessage}"
|
||||||
msgstr "Error: {errorMessage}"
|
msgstr "Error: {errorMessage}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
#: src/app/routes/_authed/admin/telemetry.tsx:63
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:260
|
#: src/features/telemetry/components/usage-screen.tsx:260
|
||||||
msgid "Errors"
|
msgid "Errors"
|
||||||
msgstr "Errors"
|
msgstr "Errors"
|
||||||
@@ -1186,7 +1186,7 @@ msgstr "Failed to set active device"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "Failed to set volume"
|
msgstr "Failed to set volume"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:42
|
#: src/features/admin/components/dashboard/tools-list.tsx:43
|
||||||
msgid "Failed to start badge recalculation"
|
msgid "Failed to start badge recalculation"
|
||||||
msgstr "Failed to start badge recalculation"
|
msgstr "Failed to start badge recalculation"
|
||||||
|
|
||||||
@@ -1198,8 +1198,8 @@ msgstr "Failed to update team"
|
|||||||
msgid "Failed to update tournament"
|
msgid "Failed to update tournament"
|
||||||
msgstr "Failed to update tournament"
|
msgstr "Failed to update tournament"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||||
#: src/features/teams/components/team-form/index.tsx:148
|
#: src/features/teams/components/team-form/index.tsx:148
|
||||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||||
msgid "Failed to upload logo"
|
msgid "Failed to upload logo"
|
||||||
@@ -1209,8 +1209,8 @@ msgstr "Failed to upload logo"
|
|||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "Failure"
|
msgstr "Failure"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||||
msgid "File too large. Maximum size is 10MB."
|
msgid "File too large. Maximum size is 10MB."
|
||||||
msgstr "File too large. Maximum size is 10MB."
|
msgstr "File too large. Maximum size is 10MB."
|
||||||
|
|
||||||
@@ -1264,7 +1264,7 @@ msgstr "Function"
|
|||||||
msgid "Function Name"
|
msgid "Function Name"
|
||||||
msgstr "Function Name"
|
msgstr "Function Name"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||||
msgid "Functions"
|
msgid "Functions"
|
||||||
msgstr "Functions"
|
msgstr "Functions"
|
||||||
|
|
||||||
@@ -1335,11 +1335,11 @@ msgstr "Have you played before?"
|
|||||||
|
|
||||||
#: src/app/routes/_authed/stats.tsx:56
|
#: src/app/routes/_authed/stats.tsx:56
|
||||||
#: src/features/bracket/components/match-dock.tsx:174
|
#: src/features/bracket/components/match-dock.tsx:174
|
||||||
#: src/features/bracket/components/match-dock.tsx:224
|
#: src/features/bracket/components/match-dock.tsx:203
|
||||||
#: src/features/matches/components/match-card.tsx:89
|
#: src/features/matches/components/match-card.tsx:89
|
||||||
#: src/features/matches/components/match-card.tsx:233
|
#: src/features/matches/components/match-card.tsx:214
|
||||||
#: src/features/players/components/league-head-to-head.tsx:198
|
#: src/features/players/components/league-head-to-head.tsx:198
|
||||||
#: src/features/players/components/profile/header.tsx:97
|
#: src/features/players/components/profile/header.tsx:79
|
||||||
msgid "Head to Head"
|
msgid "Head to Head"
|
||||||
msgstr "Head to Head"
|
msgstr "Head to Head"
|
||||||
|
|
||||||
@@ -1401,8 +1401,8 @@ msgstr "Incorrect code"
|
|||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "Invalid action"
|
msgstr "Invalid action"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||||
msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||||
|
|
||||||
@@ -1410,8 +1410,8 @@ msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
|||||||
msgid "Invalid hex color format"
|
msgid "Invalid hex color format"
|
||||||
msgstr "Invalid hex color format"
|
msgstr "Invalid hex color format"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||||
msgid "Invalid input"
|
msgid "Invalid input"
|
||||||
msgstr "Invalid input"
|
msgstr "Invalid input"
|
||||||
|
|
||||||
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
|
|||||||
msgstr "Last name must be 3-20 characters long and contain only letters"
|
msgstr "Last name must be 3-20 characters long and contain only letters"
|
||||||
|
|
||||||
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:79
|
#: src/features/admin/components/dashboard/tools-list.tsx:82
|
||||||
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
|
|
||||||
@@ -1504,8 +1504,8 @@ msgstr "Location is required"
|
|||||||
msgid "Logo"
|
msgid "Logo"
|
||||||
msgstr "Logo"
|
msgstr "Logo"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||||
msgid "Logo file is required"
|
msgid "Logo file is required"
|
||||||
msgstr "Logo file is required"
|
msgstr "Logo file is required"
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Manage Team Songs/Logos"
|
msgstr "Manage Team Songs/Logos"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:50
|
#: src/features/admin/components/dashboard/tools-list.tsx:51
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Manage Tournaments"
|
msgstr "Manage Tournaments"
|
||||||
|
|
||||||
@@ -1630,11 +1630,11 @@ msgstr "Matches for regionals are unordered"
|
|||||||
msgid "Matches Played"
|
msgid "Matches Played"
|
||||||
msgstr "Matches Played"
|
msgstr "Matches Played"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||||
msgid "Matchup"
|
msgid "Matchup"
|
||||||
msgstr "Matchup"
|
msgstr "Matchup"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||||
msgid "Matchup details"
|
msgid "Matchup details"
|
||||||
msgstr "Matchup details"
|
msgstr "Matchup details"
|
||||||
|
|
||||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Me"
|
msgstr "Me"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Migrate Badge Progress"
|
msgstr "Migrate Badge Progress"
|
||||||
|
|
||||||
@@ -1712,7 +1712,7 @@ msgstr "New client error"
|
|||||||
msgid "New Player"
|
msgid "New Player"
|
||||||
msgstr "New Player"
|
msgstr "New Player"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||||
msgid "Next match"
|
msgid "Next match"
|
||||||
msgstr "Next match"
|
msgstr "Next match"
|
||||||
|
|
||||||
@@ -1908,10 +1908,6 @@ msgstr "Once enrolled, you can view other free agents and their phone number in
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "One time code"
|
msgstr "One time code"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:95
|
|
||||||
msgid "Open Pocketbase"
|
|
||||||
msgstr "Open Pocketbase"
|
|
||||||
|
|
||||||
#: src/features/bracket/components/match-report.tsx:273
|
#: src/features/bracket/components/match-report.tsx:273
|
||||||
msgid "Opponent"
|
msgid "Opponent"
|
||||||
msgstr "Opponent"
|
msgstr "Opponent"
|
||||||
@@ -2047,7 +2043,7 @@ msgstr "Player removed successfully"
|
|||||||
msgid "Player Stats"
|
msgid "Player Stats"
|
||||||
msgstr "Player Stats"
|
msgstr "Player Stats"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:48
|
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
||||||
#: src/features/teams/components/team-profile/index.tsx:29
|
#: src/features/teams/components/team-profile/index.tsx:29
|
||||||
msgid "Players"
|
msgid "Players"
|
||||||
msgstr "Players"
|
msgstr "Players"
|
||||||
@@ -2083,7 +2079,7 @@ msgstr "Predictions"
|
|||||||
msgid "Predictions are open"
|
msgid "Predictions are open"
|
||||||
msgstr "Predictions are open"
|
msgstr "Predictions are open"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||||
msgid "Previous match"
|
msgid "Previous match"
|
||||||
msgstr "Previous match"
|
msgstr "Previous match"
|
||||||
|
|
||||||
@@ -2122,7 +2118,7 @@ msgstr "Reactions"
|
|||||||
|
|
||||||
#. placeholder {0}: migrationStatus.playersProcessed
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
#. placeholder {1}: migrationStatus.totalPlayers
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:72
|
#: src/features/admin/components/dashboard/tools-list.tsx:75
|
||||||
msgid "Recalculating badges in the background ({0}/{1})"
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
msgstr "Recalculating badges in the background ({0}/{1})"
|
msgstr "Recalculating badges in the background ({0}/{1})"
|
||||||
|
|
||||||
@@ -2366,7 +2362,7 @@ msgstr "Server functions"
|
|||||||
msgid "serverFn name"
|
msgid "serverFn name"
|
||||||
msgstr "serverFn name"
|
msgstr "serverFn name"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
#: src/app/routes/_authed/admin/telemetry.tsx:60
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:212
|
#: src/features/telemetry/components/usage-screen.tsx:212
|
||||||
msgid "Sessions"
|
msgid "Sessions"
|
||||||
msgstr "Sessions"
|
msgstr "Sessions"
|
||||||
@@ -2520,7 +2516,7 @@ msgstr "Status"
|
|||||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||||
#: src/features/login/components/player-prompt/index.tsx:115
|
#: src/features/login/components/player-prompt/index.tsx:115
|
||||||
#: src/features/login/components/player-prompt/index.tsx:126
|
#: src/features/login/components/player-prompt/index.tsx:126
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Submit"
|
msgstr "Submit"
|
||||||
@@ -2581,7 +2577,7 @@ msgstr "Team Details"
|
|||||||
msgid "Team enrolled successfully!"
|
msgid "Team enrolled successfully!"
|
||||||
msgstr "Team enrolled successfully!"
|
msgstr "Team enrolled successfully!"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:93
|
#: src/app/routes/api/teams/upload-logo.ts:94
|
||||||
#: src/features/teams/components/team-profile/index.tsx:21
|
#: src/features/teams/components/team-profile/index.tsx:21
|
||||||
msgid "Team not found"
|
msgid "Team not found"
|
||||||
msgstr "Team not found"
|
msgstr "Team not found"
|
||||||
@@ -2615,8 +2611,8 @@ msgstr "Teams"
|
|||||||
msgid "Teams created successfully!"
|
msgid "Teams created successfully!"
|
||||||
msgstr "Teams created successfully!"
|
msgstr "Teams created successfully!"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||||
msgid "Telemetry"
|
msgid "Telemetry"
|
||||||
msgstr "Telemetry"
|
msgstr "Telemetry"
|
||||||
|
|
||||||
@@ -2730,7 +2726,7 @@ msgstr "Tournament created but logo upload failed: {0}"
|
|||||||
msgid "Tournament created successfully!"
|
msgid "Tournament created successfully!"
|
||||||
msgstr "Tournament created successfully!"
|
msgstr "Tournament created successfully!"
|
||||||
|
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:94
|
#: src/app/routes/api/tournaments/upload-logo.ts:95
|
||||||
msgid "Tournament not found"
|
msgid "Tournament not found"
|
||||||
msgstr "Tournament not found"
|
msgstr "Tournament not found"
|
||||||
|
|
||||||
@@ -2784,8 +2780,8 @@ msgstr "Unenroll Team"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "Unknown error"
|
msgstr "Unknown error"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||||
msgid "Unknown error occurred"
|
msgid "Unknown error occurred"
|
||||||
msgstr "Unknown error occurred"
|
msgstr "Unknown error occurred"
|
||||||
|
|
||||||
@@ -2810,7 +2806,7 @@ msgstr "Upcoming"
|
|||||||
msgid "Update Match"
|
msgid "Update Match"
|
||||||
msgstr "Update Match"
|
msgstr "Update Match"
|
||||||
|
|
||||||
#: src/features/players/components/profile/header.tsx:92
|
#: src/features/players/components/profile/header.tsx:74
|
||||||
msgid "Update Name"
|
msgid "Update Name"
|
||||||
msgstr "Update Name"
|
msgstr "Update Name"
|
||||||
|
|
||||||
@@ -2824,7 +2820,7 @@ msgstr "Update Team"
|
|||||||
msgid "Update Tournament"
|
msgid "Update Tournament"
|
||||||
msgstr "Update Tournament"
|
msgstr "Update Tournament"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||||
msgid "Usage"
|
msgid "Usage"
|
||||||
msgstr "Usage"
|
msgstr "Usage"
|
||||||
|
|
||||||
|
|||||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Prom. Margen de Victoria"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "Otorgar insignia"
|
msgstr "Otorgar insignia"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "Otorgar insignias"
|
msgstr "Otorgar insignias"
|
||||||
|
|
||||||
@@ -606,15 +606,15 @@ msgstr "Atrás"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "Insignia otorgada a {playerName}"
|
msgstr "Insignia otorgada a {playerName}"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:91
|
#: src/features/admin/components/dashboard/tools-list.tsx:94
|
||||||
msgid "Badge recalculation failed. Check the server logs."
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
msgstr "Falló el recálculo de insignias. Revisa los registros del servidor."
|
msgstr "Falló el recálculo de insignias. Revisa los registros del servidor."
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
#: src/features/admin/components/dashboard/tools-list.tsx:40
|
||||||
msgid "Badge recalculation is already running"
|
msgid "Badge recalculation is already running"
|
||||||
msgstr "El recálculo de insignias ya está en curso"
|
msgstr "El recálculo de insignias ya está en curso"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:38
|
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
||||||
msgid "Badge recalculation started in the background"
|
msgid "Badge recalculation started in the background"
|
||||||
msgstr "Recálculo de insignias iniciado en segundo plano"
|
msgstr "Recálculo de insignias iniciado en segundo plano"
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
|||||||
msgstr "Bracket"
|
msgstr "Bracket"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:102
|
#: src/features/admin/components/dashboard/tools-list.tsx:98
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "Vista previa del bracket"
|
msgstr "Vista previa del bracket"
|
||||||
|
|
||||||
@@ -1118,7 +1118,7 @@ msgstr "Picos de errores y funciones fallando, enviados a esta cuenta"
|
|||||||
msgid "Error: {errorMessage}"
|
msgid "Error: {errorMessage}"
|
||||||
msgstr "Error: {errorMessage}"
|
msgstr "Error: {errorMessage}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
#: src/app/routes/_authed/admin/telemetry.tsx:63
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:260
|
#: src/features/telemetry/components/usage-screen.tsx:260
|
||||||
msgid "Errors"
|
msgid "Errors"
|
||||||
msgstr "Errores"
|
msgstr "Errores"
|
||||||
@@ -1186,7 +1186,7 @@ msgstr "No se pudo establecer el dispositivo activo"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "No se pudo ajustar el volumen"
|
msgstr "No se pudo ajustar el volumen"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:42
|
#: src/features/admin/components/dashboard/tools-list.tsx:43
|
||||||
msgid "Failed to start badge recalculation"
|
msgid "Failed to start badge recalculation"
|
||||||
msgstr "No se pudo iniciar el recálculo de insignias"
|
msgstr "No se pudo iniciar el recálculo de insignias"
|
||||||
|
|
||||||
@@ -1198,8 +1198,8 @@ msgstr "No se pudo actualizar el equipo"
|
|||||||
msgid "Failed to update tournament"
|
msgid "Failed to update tournament"
|
||||||
msgstr "No se pudo actualizar el torneo"
|
msgstr "No se pudo actualizar el torneo"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||||
#: src/features/teams/components/team-form/index.tsx:148
|
#: src/features/teams/components/team-form/index.tsx:148
|
||||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||||
msgid "Failed to upload logo"
|
msgid "Failed to upload logo"
|
||||||
@@ -1209,8 +1209,8 @@ msgstr "No se pudo subir el logo"
|
|||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "Fallo"
|
msgstr "Fallo"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||||
msgid "File too large. Maximum size is 10MB."
|
msgid "File too large. Maximum size is 10MB."
|
||||||
msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB."
|
msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB."
|
||||||
|
|
||||||
@@ -1264,7 +1264,7 @@ msgstr "Función"
|
|||||||
msgid "Function Name"
|
msgid "Function Name"
|
||||||
msgstr "Nombre de la función"
|
msgstr "Nombre de la función"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||||
msgid "Functions"
|
msgid "Functions"
|
||||||
msgstr "Funciones"
|
msgstr "Funciones"
|
||||||
|
|
||||||
@@ -1335,11 +1335,11 @@ msgstr "¿Ya has jugado antes?"
|
|||||||
|
|
||||||
#: src/app/routes/_authed/stats.tsx:56
|
#: src/app/routes/_authed/stats.tsx:56
|
||||||
#: src/features/bracket/components/match-dock.tsx:174
|
#: src/features/bracket/components/match-dock.tsx:174
|
||||||
#: src/features/bracket/components/match-dock.tsx:224
|
#: src/features/bracket/components/match-dock.tsx:203
|
||||||
#: src/features/matches/components/match-card.tsx:89
|
#: src/features/matches/components/match-card.tsx:89
|
||||||
#: src/features/matches/components/match-card.tsx:233
|
#: src/features/matches/components/match-card.tsx:214
|
||||||
#: src/features/players/components/league-head-to-head.tsx:198
|
#: src/features/players/components/league-head-to-head.tsx:198
|
||||||
#: src/features/players/components/profile/header.tsx:97
|
#: src/features/players/components/profile/header.tsx:79
|
||||||
msgid "Head to Head"
|
msgid "Head to Head"
|
||||||
msgstr "Cara a cara"
|
msgstr "Cara a cara"
|
||||||
|
|
||||||
@@ -1401,8 +1401,8 @@ msgstr "Código incorrecto"
|
|||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "Acción no válida"
|
msgstr "Acción no válida"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||||
msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
|
msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
|
||||||
|
|
||||||
@@ -1410,8 +1410,8 @@ msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
|
|||||||
msgid "Invalid hex color format"
|
msgid "Invalid hex color format"
|
||||||
msgstr "Formato de color hexadecimal no válido"
|
msgstr "Formato de color hexadecimal no válido"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||||
msgid "Invalid input"
|
msgid "Invalid input"
|
||||||
msgstr "Entrada no válida"
|
msgstr "Entrada no válida"
|
||||||
|
|
||||||
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
|
|||||||
msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras"
|
msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras"
|
||||||
|
|
||||||
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:79
|
#: src/features/admin/components/dashboard/tools-list.tsx:82
|
||||||
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}"
|
msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}"
|
||||||
|
|
||||||
@@ -1504,8 +1504,8 @@ msgstr "La ubicación es obligatoria"
|
|||||||
msgid "Logo"
|
msgid "Logo"
|
||||||
msgstr "Logo"
|
msgstr "Logo"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||||
msgid "Logo file is required"
|
msgid "Logo file is required"
|
||||||
msgstr "El archivo del logo es obligatorio"
|
msgstr "El archivo del logo es obligatorio"
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "Administrar canciones/logos del equipo"
|
msgstr "Administrar canciones/logos del equipo"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:50
|
#: src/features/admin/components/dashboard/tools-list.tsx:51
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "Administrar torneos"
|
msgstr "Administrar torneos"
|
||||||
|
|
||||||
@@ -1630,11 +1630,11 @@ msgstr "Los partidos de regionales no tienen un orden específico"
|
|||||||
msgid "Matches Played"
|
msgid "Matches Played"
|
||||||
msgstr "Partidos jugados"
|
msgstr "Partidos jugados"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||||
msgid "Matchup"
|
msgid "Matchup"
|
||||||
msgstr "Enfrentamiento"
|
msgstr "Enfrentamiento"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||||
msgid "Matchup details"
|
msgid "Matchup details"
|
||||||
msgstr "Detalles del enfrentamiento"
|
msgstr "Detalles del enfrentamiento"
|
||||||
|
|
||||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "Yo"
|
msgstr "Yo"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "Migrar progreso de insignias"
|
msgstr "Migrar progreso de insignias"
|
||||||
|
|
||||||
@@ -1712,7 +1712,7 @@ msgstr "Nuevo error de cliente"
|
|||||||
msgid "New Player"
|
msgid "New Player"
|
||||||
msgstr "Nuevo jugador"
|
msgstr "Nuevo jugador"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||||
msgid "Next match"
|
msgid "Next match"
|
||||||
msgstr "Siguiente partido"
|
msgstr "Siguiente partido"
|
||||||
|
|
||||||
@@ -1908,10 +1908,6 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "Código de un solo uso"
|
msgstr "Código de un solo uso"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:95
|
|
||||||
msgid "Open Pocketbase"
|
|
||||||
msgstr "Abrir Pocketbase"
|
|
||||||
|
|
||||||
#: src/features/bracket/components/match-report.tsx:273
|
#: src/features/bracket/components/match-report.tsx:273
|
||||||
msgid "Opponent"
|
msgid "Opponent"
|
||||||
msgstr "Rival"
|
msgstr "Rival"
|
||||||
@@ -2047,7 +2043,7 @@ msgstr "Jugador eliminado con éxito"
|
|||||||
msgid "Player Stats"
|
msgid "Player Stats"
|
||||||
msgstr "Estadísticas de jugador"
|
msgstr "Estadísticas de jugador"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:48
|
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
||||||
#: src/features/teams/components/team-profile/index.tsx:29
|
#: src/features/teams/components/team-profile/index.tsx:29
|
||||||
msgid "Players"
|
msgid "Players"
|
||||||
msgstr "Jugadores"
|
msgstr "Jugadores"
|
||||||
@@ -2083,7 +2079,7 @@ msgstr "Predicciones"
|
|||||||
msgid "Predictions are open"
|
msgid "Predictions are open"
|
||||||
msgstr "Las predicciones están abiertas"
|
msgstr "Las predicciones están abiertas"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||||
msgid "Previous match"
|
msgid "Previous match"
|
||||||
msgstr "Partido anterior"
|
msgstr "Partido anterior"
|
||||||
|
|
||||||
@@ -2122,7 +2118,7 @@ msgstr "Reacciones"
|
|||||||
|
|
||||||
#. placeholder {0}: migrationStatus.playersProcessed
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
#. placeholder {1}: migrationStatus.totalPlayers
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:72
|
#: src/features/admin/components/dashboard/tools-list.tsx:75
|
||||||
msgid "Recalculating badges in the background ({0}/{1})"
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
msgstr "Recalculando insignias en segundo plano ({0}/{1})"
|
msgstr "Recalculando insignias en segundo plano ({0}/{1})"
|
||||||
|
|
||||||
@@ -2366,7 +2362,7 @@ msgstr "Funciones del servidor"
|
|||||||
msgid "serverFn name"
|
msgid "serverFn name"
|
||||||
msgstr "nombre de serverFn"
|
msgstr "nombre de serverFn"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
#: src/app/routes/_authed/admin/telemetry.tsx:60
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:212
|
#: src/features/telemetry/components/usage-screen.tsx:212
|
||||||
msgid "Sessions"
|
msgid "Sessions"
|
||||||
msgstr "Sesiones"
|
msgstr "Sesiones"
|
||||||
@@ -2520,7 +2516,7 @@ msgstr "Estado"
|
|||||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||||
#: src/features/login/components/player-prompt/index.tsx:115
|
#: src/features/login/components/player-prompt/index.tsx:115
|
||||||
#: src/features/login/components/player-prompt/index.tsx:126
|
#: src/features/login/components/player-prompt/index.tsx:126
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "Enviar"
|
msgstr "Enviar"
|
||||||
@@ -2581,7 +2577,7 @@ msgstr "Detalles del equipo"
|
|||||||
msgid "Team enrolled successfully!"
|
msgid "Team enrolled successfully!"
|
||||||
msgstr "¡Equipo inscrito exitosamente!"
|
msgstr "¡Equipo inscrito exitosamente!"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:93
|
#: src/app/routes/api/teams/upload-logo.ts:94
|
||||||
#: src/features/teams/components/team-profile/index.tsx:21
|
#: src/features/teams/components/team-profile/index.tsx:21
|
||||||
msgid "Team not found"
|
msgid "Team not found"
|
||||||
msgstr "Equipo no encontrado"
|
msgstr "Equipo no encontrado"
|
||||||
@@ -2615,8 +2611,8 @@ msgstr "Equipos"
|
|||||||
msgid "Teams created successfully!"
|
msgid "Teams created successfully!"
|
||||||
msgstr "¡Equipos creados exitosamente!"
|
msgstr "¡Equipos creados exitosamente!"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||||
msgid "Telemetry"
|
msgid "Telemetry"
|
||||||
msgstr "Telemetría"
|
msgstr "Telemetría"
|
||||||
|
|
||||||
@@ -2730,7 +2726,7 @@ msgstr "Torneo creado, pero falló la carga del logo: {0}"
|
|||||||
msgid "Tournament created successfully!"
|
msgid "Tournament created successfully!"
|
||||||
msgstr "¡Torneo creado exitosamente!"
|
msgstr "¡Torneo creado exitosamente!"
|
||||||
|
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:94
|
#: src/app/routes/api/tournaments/upload-logo.ts:95
|
||||||
msgid "Tournament not found"
|
msgid "Tournament not found"
|
||||||
msgstr "Torneo no encontrado"
|
msgstr "Torneo no encontrado"
|
||||||
|
|
||||||
@@ -2784,8 +2780,8 @@ msgstr "Cancelar inscripción del equipo"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "Error desconocido"
|
msgstr "Error desconocido"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||||
msgid "Unknown error occurred"
|
msgid "Unknown error occurred"
|
||||||
msgstr "Ocurrió un error desconocido"
|
msgstr "Ocurrió un error desconocido"
|
||||||
|
|
||||||
@@ -2810,7 +2806,7 @@ msgstr "Próximos"
|
|||||||
msgid "Update Match"
|
msgid "Update Match"
|
||||||
msgstr "Actualizar partido"
|
msgstr "Actualizar partido"
|
||||||
|
|
||||||
#: src/features/players/components/profile/header.tsx:92
|
#: src/features/players/components/profile/header.tsx:74
|
||||||
msgid "Update Name"
|
msgid "Update Name"
|
||||||
msgstr "Actualizar nombre"
|
msgstr "Actualizar nombre"
|
||||||
|
|
||||||
@@ -2824,7 +2820,7 @@ msgstr "Actualizar equipo"
|
|||||||
msgid "Update Tournament"
|
msgid "Update Tournament"
|
||||||
msgstr "Actualizar torneo"
|
msgstr "Actualizar torneo"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||||
msgid "Usage"
|
msgid "Usage"
|
||||||
msgstr "Uso"
|
msgstr "Uso"
|
||||||
|
|
||||||
|
|||||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "平均勝差"
|
|||||||
msgid "Award Badge"
|
msgid "Award Badge"
|
||||||
msgstr "バッジを付与"
|
msgstr "バッジを付与"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||||
msgid "Award Badges"
|
msgid "Award Badges"
|
||||||
msgstr "バッジを付与"
|
msgstr "バッジを付与"
|
||||||
|
|
||||||
@@ -606,15 +606,15 @@ msgstr "戻る"
|
|||||||
msgid "Badge awarded to {playerName}"
|
msgid "Badge awarded to {playerName}"
|
||||||
msgstr "{playerName}にバッジを付与しました"
|
msgstr "{playerName}にバッジを付与しました"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:91
|
#: src/features/admin/components/dashboard/tools-list.tsx:94
|
||||||
msgid "Badge recalculation failed. Check the server logs."
|
msgid "Badge recalculation failed. Check the server logs."
|
||||||
msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。"
|
msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
#: src/features/admin/components/dashboard/tools-list.tsx:40
|
||||||
msgid "Badge recalculation is already running"
|
msgid "Badge recalculation is already running"
|
||||||
msgstr "バッジの再計算はすでに実行中です"
|
msgstr "バッジの再計算はすでに実行中です"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:38
|
#: src/features/admin/components/dashboard/tools-list.tsx:39
|
||||||
msgid "Badge recalculation started in the background"
|
msgid "Badge recalculation started in the background"
|
||||||
msgstr "バックグラウンドでバッジの再計算を開始しました"
|
msgstr "バックグラウンドでバッジの再計算を開始しました"
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
|||||||
msgstr "ブラケット"
|
msgstr "ブラケット"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/preview.tsx:13
|
#: src/app/routes/_authed/admin/preview.tsx:13
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:102
|
#: src/features/admin/components/dashboard/tools-list.tsx:98
|
||||||
msgid "Bracket Preview"
|
msgid "Bracket Preview"
|
||||||
msgstr "ブラケットプレビュー"
|
msgstr "ブラケットプレビュー"
|
||||||
|
|
||||||
@@ -1118,7 +1118,7 @@ msgstr "エラーの急増や失敗している関数をこのアカウントに
|
|||||||
msgid "Error: {errorMessage}"
|
msgid "Error: {errorMessage}"
|
||||||
msgstr "エラー: {errorMessage}"
|
msgstr "エラー: {errorMessage}"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
#: src/app/routes/_authed/admin/telemetry.tsx:63
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:260
|
#: src/features/telemetry/components/usage-screen.tsx:260
|
||||||
msgid "Errors"
|
msgid "Errors"
|
||||||
msgstr "エラー"
|
msgstr "エラー"
|
||||||
@@ -1186,7 +1186,7 @@ msgstr "アクティブなデバイスの設定に失敗しました"
|
|||||||
msgid "Failed to set volume"
|
msgid "Failed to set volume"
|
||||||
msgstr "音量の設定に失敗しました"
|
msgstr "音量の設定に失敗しました"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:42
|
#: src/features/admin/components/dashboard/tools-list.tsx:43
|
||||||
msgid "Failed to start badge recalculation"
|
msgid "Failed to start badge recalculation"
|
||||||
msgstr "バッジの再計算を開始できませんでした"
|
msgstr "バッジの再計算を開始できませんでした"
|
||||||
|
|
||||||
@@ -1198,8 +1198,8 @@ msgstr "チームの更新に失敗しました"
|
|||||||
msgid "Failed to update tournament"
|
msgid "Failed to update tournament"
|
||||||
msgstr "トーナメントの更新に失敗しました"
|
msgstr "トーナメントの更新に失敗しました"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||||
#: src/features/teams/components/team-form/index.tsx:148
|
#: src/features/teams/components/team-form/index.tsx:148
|
||||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||||
msgid "Failed to upload logo"
|
msgid "Failed to upload logo"
|
||||||
@@ -1209,8 +1209,8 @@ msgstr "ロゴのアップロードに失敗しました"
|
|||||||
msgid "Failure"
|
msgid "Failure"
|
||||||
msgstr "失敗"
|
msgstr "失敗"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||||
msgid "File too large. Maximum size is 10MB."
|
msgid "File too large. Maximum size is 10MB."
|
||||||
msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。"
|
msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。"
|
||||||
|
|
||||||
@@ -1264,7 +1264,7 @@ msgstr "関数"
|
|||||||
msgid "Function Name"
|
msgid "Function Name"
|
||||||
msgstr "関数名"
|
msgstr "関数名"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||||
msgid "Functions"
|
msgid "Functions"
|
||||||
msgstr "関数"
|
msgstr "関数"
|
||||||
|
|
||||||
@@ -1335,11 +1335,11 @@ msgstr "参加したことはありますか?"
|
|||||||
|
|
||||||
#: src/app/routes/_authed/stats.tsx:56
|
#: src/app/routes/_authed/stats.tsx:56
|
||||||
#: src/features/bracket/components/match-dock.tsx:174
|
#: src/features/bracket/components/match-dock.tsx:174
|
||||||
#: src/features/bracket/components/match-dock.tsx:224
|
#: src/features/bracket/components/match-dock.tsx:203
|
||||||
#: src/features/matches/components/match-card.tsx:89
|
#: src/features/matches/components/match-card.tsx:89
|
||||||
#: src/features/matches/components/match-card.tsx:233
|
#: src/features/matches/components/match-card.tsx:214
|
||||||
#: src/features/players/components/league-head-to-head.tsx:198
|
#: src/features/players/components/league-head-to-head.tsx:198
|
||||||
#: src/features/players/components/profile/header.tsx:97
|
#: src/features/players/components/profile/header.tsx:79
|
||||||
msgid "Head to Head"
|
msgid "Head to Head"
|
||||||
msgstr "対戦成績"
|
msgstr "対戦成績"
|
||||||
|
|
||||||
@@ -1401,8 +1401,8 @@ msgstr "コードが正しくありません"
|
|||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "無効な操作です"
|
msgstr "無効な操作です"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||||
msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。"
|
msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。"
|
||||||
|
|
||||||
@@ -1410,8 +1410,8 @@ msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用でき
|
|||||||
msgid "Invalid hex color format"
|
msgid "Invalid hex color format"
|
||||||
msgstr "無効なHEXカラー形式です"
|
msgstr "無効なHEXカラー形式です"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||||
msgid "Invalid input"
|
msgid "Invalid input"
|
||||||
msgstr "無効な入力です"
|
msgstr "無効な入力です"
|
||||||
|
|
||||||
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
|
|||||||
msgstr "姓は3〜20文字の英字のみで入力してください"
|
msgstr "姓は3〜20文字の英字のみで入力してください"
|
||||||
|
|
||||||
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
#. placeholder {0}: migrationStatus.result.totalBadgesEarned
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:79
|
#: src/features/admin/components/dashboard/tools-list.tsx:82
|
||||||
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||||
msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}"
|
msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}"
|
||||||
|
|
||||||
@@ -1504,8 +1504,8 @@ msgstr "場所は必須です"
|
|||||||
msgid "Logo"
|
msgid "Logo"
|
||||||
msgstr "ロゴ"
|
msgstr "ロゴ"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||||
msgid "Logo file is required"
|
msgid "Logo file is required"
|
||||||
msgstr "ロゴファイルは必須です"
|
msgstr "ロゴファイルは必須です"
|
||||||
|
|
||||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
|||||||
msgstr "チームの曲・ロゴを管理"
|
msgstr "チームの曲・ロゴを管理"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
#: src/app/routes/_authed/admin/tournaments/index.tsx:17
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:50
|
#: src/features/admin/components/dashboard/tools-list.tsx:51
|
||||||
msgid "Manage Tournaments"
|
msgid "Manage Tournaments"
|
||||||
msgstr "トーナメント管理"
|
msgstr "トーナメント管理"
|
||||||
|
|
||||||
@@ -1630,11 +1630,11 @@ msgstr "地区予選の試合は順不同です"
|
|||||||
msgid "Matches Played"
|
msgid "Matches Played"
|
||||||
msgstr "試合数"
|
msgstr "試合数"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||||
msgid "Matchup"
|
msgid "Matchup"
|
||||||
msgstr "対戦カード"
|
msgstr "対戦カード"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||||
msgid "Matchup details"
|
msgid "Matchup details"
|
||||||
msgstr "対戦カードの詳細"
|
msgstr "対戦カードの詳細"
|
||||||
|
|
||||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
|||||||
msgid "Me"
|
msgid "Me"
|
||||||
msgstr "自分"
|
msgstr "自分"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||||
msgid "Migrate Badge Progress"
|
msgid "Migrate Badge Progress"
|
||||||
msgstr "バッジ進捗を移行"
|
msgstr "バッジ進捗を移行"
|
||||||
|
|
||||||
@@ -1712,7 +1712,7 @@ msgstr "新しいクライアントエラー"
|
|||||||
msgid "New Player"
|
msgid "New Player"
|
||||||
msgstr "新しいプレイヤー"
|
msgstr "新しいプレイヤー"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||||
msgid "Next match"
|
msgid "Next match"
|
||||||
msgstr "次の試合"
|
msgstr "次の試合"
|
||||||
|
|
||||||
@@ -1908,10 +1908,6 @@ msgstr "登録が完了すると、他のフリーエージェントとその電
|
|||||||
msgid "One time code"
|
msgid "One time code"
|
||||||
msgstr "ワンタイムコード"
|
msgstr "ワンタイムコード"
|
||||||
|
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:95
|
|
||||||
msgid "Open Pocketbase"
|
|
||||||
msgstr "Pocketbaseを開く"
|
|
||||||
|
|
||||||
#: src/features/bracket/components/match-report.tsx:273
|
#: src/features/bracket/components/match-report.tsx:273
|
||||||
msgid "Opponent"
|
msgid "Opponent"
|
||||||
msgstr "対戦相手"
|
msgstr "対戦相手"
|
||||||
@@ -2047,7 +2043,7 @@ msgstr "プレイヤーを削除しました"
|
|||||||
msgid "Player Stats"
|
msgid "Player Stats"
|
||||||
msgstr "プレイヤースタッツ"
|
msgstr "プレイヤースタッツ"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:48
|
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
||||||
#: src/features/teams/components/team-profile/index.tsx:29
|
#: src/features/teams/components/team-profile/index.tsx:29
|
||||||
msgid "Players"
|
msgid "Players"
|
||||||
msgstr "プレイヤー"
|
msgstr "プレイヤー"
|
||||||
@@ -2083,7 +2079,7 @@ msgstr "予想"
|
|||||||
msgid "Predictions are open"
|
msgid "Predictions are open"
|
||||||
msgstr "予想受付中"
|
msgstr "予想受付中"
|
||||||
|
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||||
msgid "Previous match"
|
msgid "Previous match"
|
||||||
msgstr "前の試合"
|
msgstr "前の試合"
|
||||||
|
|
||||||
@@ -2122,7 +2118,7 @@ msgstr "リアクション"
|
|||||||
|
|
||||||
#. placeholder {0}: migrationStatus.playersProcessed
|
#. placeholder {0}: migrationStatus.playersProcessed
|
||||||
#. placeholder {1}: migrationStatus.totalPlayers
|
#. placeholder {1}: migrationStatus.totalPlayers
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:72
|
#: src/features/admin/components/dashboard/tools-list.tsx:75
|
||||||
msgid "Recalculating badges in the background ({0}/{1})"
|
msgid "Recalculating badges in the background ({0}/{1})"
|
||||||
msgstr "バックグラウンドでバッジを再計算中({0}/{1})"
|
msgstr "バックグラウンドでバッジを再計算中({0}/{1})"
|
||||||
|
|
||||||
@@ -2366,7 +2362,7 @@ msgstr "サーバー関数"
|
|||||||
msgid "serverFn name"
|
msgid "serverFn name"
|
||||||
msgstr "serverFn名"
|
msgstr "serverFn名"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:54
|
#: src/app/routes/_authed/admin/telemetry.tsx:60
|
||||||
#: src/features/telemetry/components/usage-screen.tsx:212
|
#: src/features/telemetry/components/usage-screen.tsx:212
|
||||||
msgid "Sessions"
|
msgid "Sessions"
|
||||||
msgstr "セッション数"
|
msgstr "セッション数"
|
||||||
@@ -2520,7 +2516,7 @@ msgstr "ステータス"
|
|||||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||||
#: src/features/login/components/player-prompt/index.tsx:115
|
#: src/features/login/components/player-prompt/index.tsx:115
|
||||||
#: src/features/login/components/player-prompt/index.tsx:126
|
#: src/features/login/components/player-prompt/index.tsx:126
|
||||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr "送信"
|
msgstr "送信"
|
||||||
@@ -2581,7 +2577,7 @@ msgstr "チーム詳細"
|
|||||||
msgid "Team enrolled successfully!"
|
msgid "Team enrolled successfully!"
|
||||||
msgstr "チームを登録しました!"
|
msgstr "チームを登録しました!"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:93
|
#: src/app/routes/api/teams/upload-logo.ts:94
|
||||||
#: src/features/teams/components/team-profile/index.tsx:21
|
#: src/features/teams/components/team-profile/index.tsx:21
|
||||||
msgid "Team not found"
|
msgid "Team not found"
|
||||||
msgstr "チームが見つかりません"
|
msgstr "チームが見つかりません"
|
||||||
@@ -2615,8 +2611,8 @@ msgstr "チーム"
|
|||||||
msgid "Teams created successfully!"
|
msgid "Teams created successfully!"
|
||||||
msgstr "チームを作成しました!"
|
msgstr "チームを作成しました!"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||||
msgid "Telemetry"
|
msgid "Telemetry"
|
||||||
msgstr "テレメトリー"
|
msgstr "テレメトリー"
|
||||||
|
|
||||||
@@ -2730,7 +2726,7 @@ msgstr "トーナメントを作成しましたが、ロゴのアップロード
|
|||||||
msgid "Tournament created successfully!"
|
msgid "Tournament created successfully!"
|
||||||
msgstr "トーナメントを作成しました!"
|
msgstr "トーナメントを作成しました!"
|
||||||
|
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:94
|
#: src/app/routes/api/tournaments/upload-logo.ts:95
|
||||||
msgid "Tournament not found"
|
msgid "Tournament not found"
|
||||||
msgstr "トーナメントが見つかりません"
|
msgstr "トーナメントが見つかりません"
|
||||||
|
|
||||||
@@ -2784,8 +2780,8 @@ msgstr "チームを登録解除"
|
|||||||
msgid "Unknown error"
|
msgid "Unknown error"
|
||||||
msgstr "不明なエラー"
|
msgstr "不明なエラー"
|
||||||
|
|
||||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||||
msgid "Unknown error occurred"
|
msgid "Unknown error occurred"
|
||||||
msgstr "不明なエラーが発生しました"
|
msgstr "不明なエラーが発生しました"
|
||||||
|
|
||||||
@@ -2810,7 +2806,7 @@ msgstr "開催予定"
|
|||||||
msgid "Update Match"
|
msgid "Update Match"
|
||||||
msgstr "試合を更新"
|
msgstr "試合を更新"
|
||||||
|
|
||||||
#: src/features/players/components/profile/header.tsx:92
|
#: src/features/players/components/profile/header.tsx:74
|
||||||
msgid "Update Name"
|
msgid "Update Name"
|
||||||
msgstr "名前を更新"
|
msgstr "名前を更新"
|
||||||
|
|
||||||
@@ -2824,7 +2820,7 @@ msgstr "チームを更新"
|
|||||||
msgid "Update Tournament"
|
msgid "Update Tournament"
|
||||||
msgstr "トーナメントを更新"
|
msgstr "トーナメントを更新"
|
||||||
|
|
||||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||||
msgid "Usage"
|
msgid "Usage"
|
||||||
msgstr "利用状況"
|
msgstr "利用状況"
|
||||||
|
|
||||||
|
|||||||
+46
-32
@@ -5,7 +5,7 @@ import {
|
|||||||
} from "@tanstack/react-start";
|
} from "@tanstack/react-start";
|
||||||
import { getRequest } from "@tanstack/react-start/server";
|
import { getRequest } from "@tanstack/react-start/server";
|
||||||
import { Logger } from "@/lib/logger";
|
import { Logger } from "@/lib/logger";
|
||||||
import { isAdminRole } from "@/features/core/utils/roles";
|
import { isAdminRole, isDeveloperRole } from "@/features/core/utils/roles";
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
const logger = new Logger("Middleware");
|
const logger = new Logger("Middleware");
|
||||||
@@ -70,41 +70,55 @@ export const superTokensFunctionMiddleware = createMiddleware({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const superTokensAdminFunctionMiddleware = createMiddleware({
|
const createRoleFunctionMiddleware = (
|
||||||
type: "function",
|
hasRole: (roles?: string[] | null) => boolean,
|
||||||
}).server(async ({ next, serverFnMeta }) => {
|
label: string
|
||||||
const request = getRequest();
|
) =>
|
||||||
|
createMiddleware({
|
||||||
|
type: "function",
|
||||||
|
}).server(async ({ next, serverFnMeta }) => {
|
||||||
|
const request = getRequest();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const context = await getSessionContext(request, { isServerFunction: true });
|
const context = await getSessionContext(request, { isServerFunction: true });
|
||||||
|
|
||||||
if (isAdminRole(context.roles)) {
|
if (hasRole(context.roles)) {
|
||||||
return next({ context });
|
return next({ context });
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error(`Unauthorized user in ${label} function.`, {
|
||||||
|
userAuthId: context.userAuthId,
|
||||||
|
roles: context.roles,
|
||||||
|
});
|
||||||
|
const { recordDeniedServerFn } = await import("./activities");
|
||||||
|
recordDeniedServerFn(request, context.metadata, serverFnMeta);
|
||||||
|
throw new Error("Unauthorized");
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.message === "SESSION_REFRESH_REQUIRED") {
|
||||||
|
throw new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: "SESSION_REFRESH_REQUIRED",
|
||||||
|
message: "Session needs to be refreshed"
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 401,
|
||||||
|
headers: { "Content-Type": "application/json" }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
logger.error("Unauthorized user in admin function.", {
|
export const superTokensAdminFunctionMiddleware = createRoleFunctionMiddleware(
|
||||||
userAuthId: context.userAuthId,
|
isAdminRole,
|
||||||
roles: context.roles,
|
"admin"
|
||||||
});
|
);
|
||||||
const { recordDeniedServerFn } = await import("./activities");
|
|
||||||
recordDeniedServerFn(request, context.metadata, serverFnMeta);
|
export const superTokensDeveloperFunctionMiddleware = createRoleFunctionMiddleware(
|
||||||
throw new Error("Unauthorized");
|
isDeveloperRole,
|
||||||
} catch (error: any) {
|
"developer"
|
||||||
if (error.message === "SESSION_REFRESH_REQUIRED") {
|
);
|
||||||
throw new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
error: "SESSION_REFRESH_REQUIRED",
|
|
||||||
message: "Session needs to be refreshed"
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
status: 401,
|
|
||||||
headers: { "Content-Type": "application/json" }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setUserMetadata = createServerFn({ method: "POST" })
|
export const setUserMetadata = createServerFn({ method: "POST" })
|
||||||
.validator(
|
.validator(
|
||||||
|
|||||||
Reference in New Issue
Block a user