mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-07 03:35:40 -05:00
Notification for canceling scrim
This commit is contained in:
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
@@ -9,6 +9,8 @@
|
||||
|
||||
- `npm run typecheck` runs TypeScript type checking
|
||||
- `npm run biome:fix` runs Biome code formatter and linter
|
||||
- `npm run test:unit` runs all unit tests
|
||||
- `npm run i18n:sync` syncs translation jsons with English and should always be run after adding new text to an English translation file
|
||||
|
||||
## Typescript
|
||||
|
||||
@@ -46,7 +48,12 @@
|
||||
- down migrations are not needed, only up migrations
|
||||
- every database id is of type number
|
||||
|
||||
## Playwright
|
||||
## E2E testing
|
||||
|
||||
- library used for E2E testing is Playwright
|
||||
- `page.goto` is forbidden, use the `navigate` function to do a page navigation
|
||||
- to submit a form you use the `submit` function
|
||||
|
||||
## Unit testing
|
||||
|
||||
- library used for unit testing is Vitest
|
||||
|
||||
@@ -62,7 +62,8 @@ export type Notification =
|
||||
>
|
||||
| NotificationItem<"SEASON_STARTED", { seasonNth: number }>
|
||||
| NotificationItem<"SCRIM_NEW_REQUEST", { fromUsername: string }>
|
||||
| NotificationItem<"SCRIM_SCHEDULED", { id: number; at: number }>;
|
||||
| NotificationItem<"SCRIM_SCHEDULED", { id: number; at: number }>
|
||||
| NotificationItem<"SCRIM_CANCELED", { id: number; at: number }>;
|
||||
|
||||
type NotificationItem<
|
||||
T extends string,
|
||||
|
||||
@@ -35,6 +35,7 @@ export const notificationNavIcon = (type: Notification["type"]) => {
|
||||
return "medal";
|
||||
case "SCRIM_NEW_REQUEST":
|
||||
case "SCRIM_SCHEDULED":
|
||||
case "SCRIM_CANCELED":
|
||||
return "scrims";
|
||||
default:
|
||||
assertUnreachable(type);
|
||||
@@ -78,6 +79,7 @@ export const notificationLink = (notification: Notification) => {
|
||||
case "SCRIM_NEW_REQUEST": {
|
||||
return scrimsPage();
|
||||
}
|
||||
case "SCRIM_CANCELED":
|
||||
case "SCRIM_SCHEDULED": {
|
||||
return scrimPage(notification.meta.id);
|
||||
}
|
||||
@@ -91,7 +93,10 @@ export const mapMetaForTranslation = (
|
||||
notification: Notification,
|
||||
language: string,
|
||||
) => {
|
||||
if (notification.type === "SCRIM_SCHEDULED") {
|
||||
if (
|
||||
notification.type === "SCRIM_SCHEDULED" ||
|
||||
notification.type === "SCRIM_CANCELED"
|
||||
) {
|
||||
return {
|
||||
...notification.meta,
|
||||
timeString: notification.meta.at // TODO: after two weeks this check can be removed (all notifications will have `at`)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ActionFunctionArgs } from "@remix-run/node";
|
||||
import { notify } from "~/features/notifications/core/notify.server";
|
||||
import { requirePermission } from "~/modules/permissions/guards.server";
|
||||
import {
|
||||
notFoundIfFalsy,
|
||||
@@ -6,10 +7,14 @@ import {
|
||||
parseRequestPayload,
|
||||
} from "~/utils/remix.server";
|
||||
import { idObject } from "~/utils/zod";
|
||||
import { databaseTimestampToDate } from "../../../utils/dates";
|
||||
import {
|
||||
databaseTimestampToDate,
|
||||
databaseTimestampToJavascriptTimestamp,
|
||||
} from "../../../utils/dates";
|
||||
import { errorToast } from "../../../utils/remix.server";
|
||||
import { requireUser } from "../../auth/core/user.server";
|
||||
import * as ScrimPostRepository from "../ScrimPostRepository.server";
|
||||
import * as Scrim from "../core/Scrim";
|
||||
import { cancelScrimSchema } from "../scrims-schemas";
|
||||
|
||||
export const action = async ({ request, params }: ActionFunctionArgs) => {
|
||||
@@ -33,5 +38,17 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
|
||||
reason: data.reason,
|
||||
});
|
||||
|
||||
notify({
|
||||
userIds: Scrim.participantIdsListFromAccepted(post),
|
||||
defaultSeenUserIds: [user.id],
|
||||
notification: {
|
||||
type: "SCRIM_CANCELED",
|
||||
meta: {
|
||||
id: post.id,
|
||||
at: databaseTimestampToJavascriptTimestamp(post.at),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
78
app/features/scrims/core/Scrim.test.ts
Normal file
78
app/features/scrims/core/Scrim.test.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ScrimPost } from "../scrims-types";
|
||||
import { participantIdsListFromAccepted } from "./Scrim";
|
||||
|
||||
type MockUser = { id: number };
|
||||
type MockRequest = { isAccepted: boolean; users: MockUser[] };
|
||||
|
||||
function createPost(users: MockUser[], requests: MockRequest[]): ScrimPost {
|
||||
return {
|
||||
id: 1,
|
||||
users,
|
||||
requests,
|
||||
createdAt: "",
|
||||
updatedAt: "",
|
||||
title: "",
|
||||
description: "",
|
||||
status: "open",
|
||||
authorId: 0,
|
||||
} as unknown as ScrimPost;
|
||||
}
|
||||
|
||||
describe("participantIdsListFromAccepted", () => {
|
||||
it("returns only post users if no accepted request", () => {
|
||||
const post = createPost(
|
||||
[{ id: 10 }, { id: 20 }],
|
||||
[
|
||||
{
|
||||
isAccepted: false,
|
||||
users: [{ id: 30 }],
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const result = participantIdsListFromAccepted(post);
|
||||
expect(result).toEqual([10, 20]);
|
||||
});
|
||||
|
||||
it("returns post users and accepted request users", () => {
|
||||
const post = createPost(
|
||||
[{ id: 10 }, { id: 20 }],
|
||||
[
|
||||
{
|
||||
isAccepted: false,
|
||||
users: [{ id: 30 }],
|
||||
},
|
||||
{
|
||||
isAccepted: true,
|
||||
users: [{ id: 40 }, { id: 50 }],
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const result = participantIdsListFromAccepted(post);
|
||||
expect(result).toEqual([10, 20, 40, 50]);
|
||||
});
|
||||
|
||||
it("returns post users if accepted request has no users", () => {
|
||||
const post = createPost(
|
||||
[{ id: 10 }],
|
||||
[
|
||||
{
|
||||
isAccepted: true,
|
||||
users: [],
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const result = participantIdsListFromAccepted(post);
|
||||
expect(result).toEqual([10]);
|
||||
});
|
||||
|
||||
it("returns empty array if no users and no accepted request", () => {
|
||||
const post = createPost([], []);
|
||||
|
||||
const result = participantIdsListFromAccepted(post);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { logger } from "~/utils/logger";
|
||||
import type { ScrimPost } from "../scrims-types";
|
||||
|
||||
/** Returns true if the original poster has accepted any of the requests. */
|
||||
@@ -17,3 +18,20 @@ export function isParticipating(post: ScrimPost, userId: number) {
|
||||
export function resolvePoolCode(postId: number) {
|
||||
return `SC${postId % 10}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of participant IDs from the given post object that is accepted (scrim page exists).
|
||||
*/
|
||||
export function participantIdsListFromAccepted(post: ScrimPost) {
|
||||
const acceptedRequest = post.requests.find((r) => r.isAccepted);
|
||||
|
||||
if (!acceptedRequest) {
|
||||
logger.warn(
|
||||
`Scrim post ${post.id} has no accepted request, returning only post users.`,
|
||||
);
|
||||
}
|
||||
|
||||
return post.users
|
||||
.map((u) => u.id)
|
||||
.concat(acceptedRequest?.users.map((u) => u.id) ?? []);
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
return {
|
||||
post,
|
||||
chatUsers: await UserRepository.findChatUsersByUserIds(
|
||||
post.users
|
||||
.map((u) => u.id)
|
||||
.concat(post.requests.at(0)?.users.map((u) => u.id) ?? []),
|
||||
Scrim.participantIdsListFromAccepted(post),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "logindforsøg afbrudt",
|
||||
"auth.errors.failed": "Loginforsøg fejlet",
|
||||
"auth.errors.discordPermissions": "Før at du kan oprette en profil på sendou.ink, skal sendou.ink have adgang til din Discordprofils navn, brugerbillede og sociale forbindelser (de sociale medier, som du har tilknyttet din discordprofil).",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Einloggen abgebrochen",
|
||||
"auth.errors.failed": "Einloggen fehlgeschlagen",
|
||||
"auth.errors.discordPermissions": "Für dein sendou.ink-Profil benötigt die Seite Zugriff auf den Namen, Avatar und verbundene Social-Media-Accounts in deinem Discord-Profil.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "Pickup von {{username}}",
|
||||
"status.booked": "Gebucht",
|
||||
"status.pending": "Ausstehend",
|
||||
"status.canceled": "",
|
||||
"actions.request": "Anfragen",
|
||||
"deleteModal.title": "Scrim post löschen?",
|
||||
"deleteModal.prevented": "Der Post muss von Ersteller ({{username}}) gelöscht werden.",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} requested a scrim",
|
||||
"notifications.title.SCRIM_SCHEDULED": "Scrim Scheduled",
|
||||
"notifications.text.SCRIM_SCHEDULED": "New scrim scheduled at {{timeString}}",
|
||||
"notifications.title.SCRIM_CANCELED": "Scrim Canceled",
|
||||
"notifications.text.SCRIM_CANCELED": "The scrim at {{timeString}} was canceled",
|
||||
"auth.errors.aborted": "Login Aborted",
|
||||
"auth.errors.failed": "Login Failed",
|
||||
"auth.errors.discordPermissions": "For your sendou.ink profile, the site needs access to your Discord profile's name, avatar and social connections.",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Ingreso cancelado",
|
||||
"auth.errors.failed": "Ingreso fallido",
|
||||
"auth.errors.discordPermissions": "Para tu perfil en sendou.ink, el sitio requiere aceso a tu nombre en Discord, avatar, y redes sociales.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Ingreso cancelado",
|
||||
"auth.errors.failed": "Ingreso fallido",
|
||||
"auth.errors.discordPermissions": "Para tu perfil en sendou.ink, el sitio requiere aceso a tu nombre en Discord, avatar, y redes sociales.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Connexion abandonnée",
|
||||
"auth.errors.failed": "Connexion échouée",
|
||||
"auth.errors.discordPermissions": "Pour mettre en place votre profil, sendou.ink a besoin de votre nom de profil Discord, de votre avatar et de vos réseaux connectés.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} vous demande de scrim",
|
||||
"notifications.title.SCRIM_SCHEDULED": "Scrim Programmé",
|
||||
"notifications.text.SCRIM_SCHEDULED": "Nouveau scrim programmé à {{timeString}}",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Connexion abandonnée",
|
||||
"auth.errors.failed": "Connexion échouée",
|
||||
"auth.errors.discordPermissions": "Pour mettre en place votre profil, sendou.ink a besoin de votre nom de profil Discord, de votre avatar et de vos réseaux connectés.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "Pickup de {{username}}",
|
||||
"status.booked": "Réservé",
|
||||
"status.pending": "En attente",
|
||||
"status.canceled": "",
|
||||
"actions.request": "Demande",
|
||||
"deleteModal.title": "Supprimer le post?",
|
||||
"deleteModal.prevented": "Le post doit être supprimée par le propriétaire ({{username}})",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "הכניסה בוטלה",
|
||||
"auth.errors.failed": "הכניסה נכשלה",
|
||||
"auth.errors.discordPermissions": "עבור פרופיל sendou.ink שלך, האתר זקוק לגישה לשם, הפרופיל והקשרים החברתיים של פרופיל ה-Discord שלך.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Accesso cancellato",
|
||||
"auth.errors.failed": "Accesso fallito",
|
||||
"auth.errors.discordPermissions": "Per il tuo profilo di sendou.ink, il sito ha bisogno di accesso al nome utente, avatar e connessioni social del tuo profilo Discord.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "ログインを中断しました",
|
||||
"auth.errors.failed": "ログインに失敗しました",
|
||||
"auth.errors.discordPermissions": "sendou.ink は、Discord のプロファイル名、アバター、SNS連携をサイトのプロファイルに使用します。",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "로그인 중단됨",
|
||||
"auth.errors.failed": "로그인 실패",
|
||||
"auth.errors.discordPermissions": "sendou.ink 프로필을 위해 디스코드 프로필의 이름, 아바타와 연락처에 대한 접근이 필요합니다.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "",
|
||||
"auth.errors.failed": "",
|
||||
"auth.errors.discordPermissions": "",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Logowanie przerwane",
|
||||
"auth.errors.failed": "Logowanie nieudane",
|
||||
"auth.errors.discordPermissions": "Do twojego profilu sendou.ink, ta strona potrzebuje dostęp do twojej nazwy, avataru i połączeń konta Discord.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Login Abortado",
|
||||
"auth.errors.failed": "Login Falhou",
|
||||
"auth.errors.discordPermissions": "Para o seu perfil do sendou.ink, o site precisa de acesso ao nome do perfil do seu Discord, incluindo também o avatar e conexões sociais.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} запросил скрим",
|
||||
"notifications.title.SCRIM_SCHEDULED": "Скрим Запланирован",
|
||||
"notifications.text.SCRIM_SCHEDULED": "Новый скрим запланирован на {{timeString}}",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "Вход отменён",
|
||||
"auth.errors.failed": "Ошибка входа",
|
||||
"auth.errors.discordPermissions": "Для вашего профиля на sendou.ink странице нужен доступ к вашему имени, аватару и привязанным аккаунтам соц. сетей в Discord.",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
"notifications.text.SCRIM_NEW_REQUEST": "",
|
||||
"notifications.title.SCRIM_SCHEDULED": "",
|
||||
"notifications.text.SCRIM_SCHEDULED": "",
|
||||
"notifications.title.SCRIM_CANCELED": "",
|
||||
"notifications.text.SCRIM_CANCELED": "",
|
||||
"auth.errors.aborted": "登录中止",
|
||||
"auth.errors.failed": "登录失败",
|
||||
"auth.errors.discordPermissions": "为了完善您的sendou.ink个人资料,网站需要获取您的Discord名字、头像和社交链接。",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"pickup": "",
|
||||
"status.booked": "",
|
||||
"status.pending": "",
|
||||
"status.canceled": "",
|
||||
"actions.request": "",
|
||||
"deleteModal.title": "",
|
||||
"deleteModal.prevented": "",
|
||||
|
||||
Reference in New Issue
Block a user