diff --git a/app/(admin)/(activity-management)/cheat-records/[id]/_components/details.tsx b/app/(admin)/(activity-management)/cheat-records/[id]/_components/details.tsx
new file mode 100644
index 0000000..3893109
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/[id]/_components/details.tsx
@@ -0,0 +1,135 @@
+"use client";
+
+import AppAvatar from "@/components/avatar";
+import { CardLayout } from "@/components/card-layout";
+import { Badge } from "@/components/ui/badge";
+import { StyledLink } from "@/components/ui/link";
+import { type FragmentType, graphql, useFragment } from "@/gql";
+import { useSuspenseQuery } from "@apollo/client/react";
+
+const CHEAT_RECORD_DETAILS_QUERY = graphql(`
+ query CheatRecordDetails($id: ID!) {
+ cheatRecord(id: $id) {
+ ...CheatRecordDetailsCard
+ id
+ }
+ }
+`);
+
+const CHEAT_RECORD_DETAILS_FRAGMENT = graphql(`
+ fragment CheatRecordDetailsCard on CheatRecord {
+ id
+ cheatedAt
+ reason
+ resolvedAt
+ resolvedReason
+ user {
+ id
+ avatar
+ email
+ name
+ }
+ }
+`);
+
+export function CheatRecordDetails({ id }: { id: string }) {
+ const { data } = useSuspenseQuery(CHEAT_RECORD_DETAILS_QUERY, {
+ variables: { id },
+ });
+
+ const fragment = data.cheatRecord;
+
+ return (
+
+
+
+ );
+}
+
+function DetailsCard({
+ fragment,
+}: {
+ fragment: FragmentType;
+}) {
+ const {
+ reason,
+ cheatedAt,
+ resolvedAt,
+ resolvedReason,
+ user,
+ } = useFragment(CHEAT_RECORD_DETAILS_FRAGMENT, fragment);
+
+ const isResolved = !!resolvedAt;
+
+ return (
+
+
+
+
狀態
+ {isResolved
+ ? (
+
+ 已解決
+
+ )
+ :
未解決}
+
+
+
+
使用者
+
+
+
+
+ {user.name}
+
+
{user.email}
+
+
+
+
+
+
+
+
作弊時間
+
+ {new Date(cheatedAt).toLocaleString("zh-TW", {
+ timeZone: "Asia/Taipei",
+ })}
+
+
+
+ {isResolved && (
+ <>
+
+
解決時間
+
+ {new Date(resolvedAt).toLocaleString("zh-TW", {
+ timeZone: "Asia/Taipei",
+ })}
+
+
+
+ {resolvedReason && (
+
+
解決原因
+
{resolvedReason}
+
+ )}
+ >
+ )}
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/[id]/_components/header.tsx b/app/(admin)/(activity-management)/cheat-records/[id]/_components/header.tsx
new file mode 100644
index 0000000..9d8ae89
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/[id]/_components/header.tsx
@@ -0,0 +1,59 @@
+"use client";
+
+import AppAvatar from "@/components/avatar";
+import PageHeader, { PageHeaderSkeleton } from "@/components/page-header";
+import { graphql } from "@/gql";
+import { useSuspenseQuery } from "@apollo/client/react";
+import { Suspense } from "react";
+
+const CHEAT_RECORD_HEADER_QUERY = graphql(`
+ query CheatRecordHeader($id: ID!) {
+ cheatRecord(id: $id) {
+ id
+ reason
+ user {
+ id
+ avatar
+ email
+ name
+ }
+ }
+ }
+`);
+
+export function Header({ id }: { id: string }) {
+ return (
+ }>
+
+
+ );
+}
+
+function HeaderMain({ id }: { id: string }) {
+ const { data } = useSuspenseQuery(CHEAT_RECORD_HEADER_QUERY, {
+ variables: { id },
+ });
+
+ return (
+
+ );
+}
+
+function HeaderSkeleton() {
+ return (
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/[id]/_components/resolve-button.tsx b/app/(admin)/(activity-management)/cheat-records/[id]/_components/resolve-button.tsx
new file mode 100644
index 0000000..c53e7ba
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/[id]/_components/resolve-button.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+import { graphql } from "@/gql";
+import { useSuspenseQuery } from "@apollo/client/react";
+import { ResolveCheatRecordButtonTrigger } from "../../_components/resolve";
+
+const CHEAT_RECORD_RESOLVE_BUTTON_QUERY = graphql(`
+ query CheatRecordResolveButton($id: ID!) {
+ cheatRecord(id: $id) {
+ id
+ resolvedAt
+ }
+ }
+`);
+
+export function ResolveButton({ id }: { id: string }) {
+ const { data } = useSuspenseQuery(CHEAT_RECORD_RESOLVE_BUTTON_QUERY, {
+ variables: { id },
+ });
+
+ if (data.cheatRecord.resolvedAt) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/[id]/page.tsx b/app/(admin)/(activity-management)/cheat-records/[id]/page.tsx
new file mode 100644
index 0000000..d6a0f51
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/[id]/page.tsx
@@ -0,0 +1,48 @@
+import { SiteHeader } from "@/components/site-header";
+import type { Metadata } from "next";
+import { Suspense } from "react";
+import { CheatRecordDetails } from "./_components/details";
+import { Header } from "./_components/header";
+import { ResolveButton } from "./_components/resolve-button";
+
+export const metadata: Metadata = {
+ title: "作弊記錄詳情",
+};
+
+export default async function CheatRecordPage({
+ params,
+}: {
+ params: Promise<{ id: string }>;
+}) {
+ const { id } = await params;
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/create-form.tsx b/app/(admin)/(activity-management)/cheat-records/_components/create-form.tsx
new file mode 100644
index 0000000..ecb2e64
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/create-form.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
+import { Input } from "@/components/ui/input";
+import { Spinner } from "@/components/ui/spinner";
+import { Textarea } from "@/components/ui/textarea";
+import { graphql } from "@/gql";
+import { skipToken, useQuery } from "@apollo/client/react";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useDebouncedValue } from "foxact/use-debounced-value";
+import { useEffect } from "react";
+import { useForm, useWatch } from "react-hook-form";
+import { z } from "zod";
+
+const createCheatRecordFormSchema = z.object({
+ reason: z.string().min(1, "請輸入原因"),
+ userID: z.string().min(1, "請輸入使用者 ID"),
+});
+
+const CREATE_CHEAT_RECORD_FORM_USER_INFO_QUERY = graphql(`
+ query CreateCheatRecordFormUserInfo($id: ID!) {
+ user(id: $id) {
+ id
+ email
+ name
+ }
+ }
+`);
+
+export type CreateCheatRecordFormData = z.infer<
+ typeof createCheatRecordFormSchema
+>;
+
+export function CreateCheatRecordForm({
+ defaultValues,
+ onSubmit,
+ onFormStateChange,
+}: {
+ defaultValues: CreateCheatRecordFormData;
+ onSubmit: (data: CreateCheatRecordFormData) => void;
+ onFormStateChange?: (isDirty: boolean) => void;
+}) {
+ const form = useForm({
+ resolver: zodResolver(createCheatRecordFormSchema),
+ defaultValues,
+ });
+
+ const isDirty = form.formState.isDirty;
+
+ useEffect(() => {
+ onFormStateChange?.(isDirty);
+ }, [isDirty, onFormStateChange]);
+
+ const userID = useWatch({ control: form.control, name: "userID" });
+ const userIDDebounced = useDebouncedValue(userID, 200);
+
+ const { data: userInfoData, loading } = useQuery(
+ CREATE_CHEAT_RECORD_FORM_USER_INFO_QUERY,
+ userIDDebounced
+ ? {
+ variables: {
+ id: userIDDebounced,
+ },
+ errorPolicy: "ignore",
+ }
+ : skipToken,
+ );
+
+ return (
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/create.tsx b/app/(admin)/(activity-management)/cheat-records/_components/create.tsx
new file mode 100644
index 0000000..016447c
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/create.tsx
@@ -0,0 +1,142 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { ConfirmationDialog } from "@/components/ui/confirmation-dialog";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { useDialogCloseConfirmation } from "@/hooks/use-dialog-close-confirmation";
+import { useMutation } from "@apollo/client/react";
+import { Plus } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { Suspense, useState } from "react";
+import { toast } from "sonner";
+import { CreateCheatRecordForm, type CreateCheatRecordFormData } from "./create-form";
+import { CREATE_CHEAT_RECORD_MUTATION } from "./mutation";
+import { CHEAT_RECORDS_TABLE_QUERY } from "./query";
+
+export function CreateCheatRecordButtonTrigger({
+ userId,
+}: {
+ userId?: string;
+}) {
+ const router = useRouter();
+ const [open, setOpen] = useState(false);
+ const [isFormDirty, setIsFormDirty] = useState(false);
+
+ const {
+ showConfirmation,
+ handleDialogOpenChange,
+ handleConfirmClose,
+ handleCancelClose,
+ } = useDialogCloseConfirmation({
+ isDirty: isFormDirty,
+ setOpen,
+ onConfirmedClose: () => {
+ setIsFormDirty(false);
+ },
+ });
+
+ const handleFormStateChange = (isDirty: boolean) => {
+ setIsFormDirty(isDirty);
+ };
+
+ const handleCompleted = () => {
+ setIsFormDirty(false);
+ setOpen(false);
+ router.refresh();
+ };
+
+ return (
+ <>
+
+
+ {}}
+ onConfirm={handleConfirmClose}
+ onCancel={handleCancelClose}
+ />
+ >
+ );
+}
+
+function CreateCheatRecordDialogContent({
+ userId,
+ onCompleted,
+ onFormStateChange,
+}: {
+ userId?: string;
+ onCompleted: () => void;
+ onFormStateChange: (isDirty: boolean) => void;
+}) {
+ const [createCheatRecord] = useMutation(CREATE_CHEAT_RECORD_MUTATION, {
+ refetchQueries: [{ query: CHEAT_RECORDS_TABLE_QUERY }],
+ awaitRefetchQueries: true,
+
+ onError(error) {
+ toast.error("新增作弊記錄失敗", {
+ description: error.message,
+ });
+ },
+
+ onCompleted() {
+ toast.success("作弊記錄已新增");
+ onCompleted();
+ },
+ });
+
+ const onSubmit = (data: CreateCheatRecordFormData) => {
+ try {
+ createCheatRecord({
+ variables: {
+ reason: data.reason,
+ userID: data.userID,
+ },
+ });
+ } catch (error) {
+ toast.error("新增作弊記錄失敗", {
+ description: error instanceof Error ? error.message : "未知錯誤",
+ });
+ }
+ };
+
+ return (
+
+
+ 新增作弊記錄
+
+ 為使用者新增一個作弊記錄,並提供原因。
+
+
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/data-table-columns.tsx b/app/(admin)/(activity-management)/cheat-records/_components/data-table-columns.tsx
new file mode 100644
index 0000000..bc1865a
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/data-table-columns.tsx
@@ -0,0 +1,140 @@
+import AppAvatar from "@/components/avatar";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { StyledLink } from "@/components/ui/link";
+import type { ColumnDef } from "@tanstack/react-table";
+import { MoreHorizontal } from "lucide-react";
+import Link from "next/link";
+import { ResolveCheatRecordDropdownTrigger } from "./resolve";
+
+export interface CheatRecord {
+ id: string;
+ reason: string;
+ cheatedAt: string;
+ resolvedAt?: string | null;
+ resolvedReason?: string | null;
+ user: {
+ id: string;
+ name: string;
+ email: string;
+ avatar?: string | null;
+ };
+}
+
+export const columns: ColumnDef[] = [
+ {
+ accessorKey: "id",
+ header: "ID",
+ cell: ({ row }) => {
+ const record = row.original;
+
+ return (
+
+ {record.id}
+
+ );
+ },
+ },
+ {
+ accessorKey: "user",
+ header: "使用者",
+ cell: ({ row }) => {
+ const user = row.original.user;
+ return (
+
+
+
+
+ {user.name}
+
+
{user.email}
+
+
+ );
+ },
+ },
+ {
+ accessorKey: "reason",
+ header: "原因",
+ cell: ({ row }) => {
+ return {row.original.reason}
;
+ },
+ },
+ {
+ accessorKey: "cheatedAt",
+ header: "作弊時間",
+ cell: ({ row }) => {
+ const cheatedAt = new Date(row.original.cheatedAt);
+ return {cheatedAt.toLocaleString("zh-TW")}
;
+ },
+ },
+ {
+ id: "status",
+ accessorKey: "resolvedAt",
+ header: "狀態",
+ cell: ({ row }) => {
+ const resolvedAt = row.original.resolvedAt;
+ if (resolvedAt) {
+ return (
+
+ 已解決
+
+ );
+ }
+ return 未解決;
+ },
+ },
+ {
+ id: "resolvedAtTime",
+ accessorKey: "resolvedAt",
+ header: "解決時間",
+ cell: ({ row }) => {
+ const resolvedAt = row.original.resolvedAt;
+ if (resolvedAt) {
+ return {new Date(resolvedAt).toLocaleString("zh-TW")}
;
+ }
+ return -
;
+ },
+ },
+ {
+ id: "actions",
+ cell: ({ row }) => {
+ const record = row.original;
+ const isResolved = !!record.resolvedAt;
+
+ return (
+
+
+
+
+
+ 動作
+
+ 檢視記錄
+
+
+ 檢視使用者
+
+ {!isResolved && (
+ <>
+
+
+ >
+ )}
+
+
+ );
+ },
+ },
+];
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/data-table.tsx b/app/(admin)/(activity-management)/cheat-records/_components/data-table.tsx
new file mode 100644
index 0000000..245bc03
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/data-table.tsx
@@ -0,0 +1,83 @@
+"use client";
+
+import { CursorDataTable } from "@/components/data-table/cursor";
+import type { Direction } from "@/components/data-table/pagination";
+import { useSuspenseQuery } from "@apollo/client/react";
+import type { VariablesOf } from "@graphql-typed-document-node/core";
+import { useState } from "react";
+import { type CheatRecord, columns } from "./data-table-columns";
+import { CHEAT_RECORDS_TABLE_QUERY } from "./query";
+
+export function CheatRecordsDataTable({ query }: { query?: string }) {
+ const PAGE_SIZE = 20;
+ const [cursors, setCursors] = useState<(string | null)[]>([null]);
+ const [currentIndex, setCurrentIndex] = useState(0);
+
+ const after = cursors[currentIndex];
+
+ const variables = {
+ first: PAGE_SIZE,
+ after,
+ where: query
+ ? {
+ or: [
+ { reasonContains: query },
+ { hasUserWith: [{ nameContains: query }] },
+ { hasUserWith: [{ emailContains: query }] },
+ ],
+ }
+ : undefined,
+ } satisfies VariablesOf;
+
+ const { data } = useSuspenseQuery(CHEAT_RECORDS_TABLE_QUERY, {
+ variables,
+ });
+
+ const recordList = data?.cheatRecords.edges
+ ?.map((edge) => {
+ const record = edge?.node;
+ if (!record) return null;
+ return {
+ id: record.id,
+ reason: record.reason,
+ cheatedAt: record.cheatedAt,
+ resolvedAt: record.resolvedAt,
+ resolvedReason: record.resolvedReason,
+ user: {
+ id: record.user.id,
+ name: record.user.name,
+ email: record.user.email,
+ avatar: record.user.avatar,
+ },
+ } satisfies CheatRecord;
+ })
+ .filter((record) => record !== null) ?? [];
+
+ const pageInfo = data?.cheatRecords.pageInfo;
+
+ const handlePageChange = (direction: Direction) => {
+ if (!pageInfo) return;
+ if (direction === "forward" && pageInfo.hasNextPage) {
+ const nextCursor = pageInfo.endCursor ?? null;
+ setCursors(prev => {
+ const newCursors = prev.slice(0, currentIndex + 1);
+ newCursors.push(nextCursor);
+ return newCursors;
+ });
+ setCurrentIndex(currentIndex + 1);
+ } else if (direction === "backward" && currentIndex > 0) {
+ setCurrentIndex(currentIndex - 1);
+ }
+ };
+
+ return (
+ 0}
+ onPageChange={handlePageChange}
+ />
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/filterable-data-table.tsx b/app/(admin)/(activity-management)/cheat-records/_components/filterable-data-table.tsx
new file mode 100644
index 0000000..49d1c8a
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/filterable-data-table.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import { Input } from "@/components/ui/input";
+
+import { DataTableSkeleton } from "@/components/data-table/skeleton";
+import { useDebouncedValue } from "foxact/use-debounced-value";
+import { Suspense, useState } from "react";
+import { CheatRecordsDataTable } from "./data-table";
+
+export default function FilterableDataTable() {
+ const [query, setQuery] = useState("");
+ const debouncedQuery = useDebouncedValue(query, 200);
+
+ return (
+
+
+ setQuery(e.target.value)}
+ />
+
+
+
}>
+
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/mutation.ts b/app/(admin)/(activity-management)/cheat-records/_components/mutation.ts
new file mode 100644
index 0000000..7e48f98
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/mutation.ts
@@ -0,0 +1,22 @@
+import { graphql } from "@/gql";
+
+export const CREATE_CHEAT_RECORD_MUTATION = graphql(`
+ mutation CreateCheatRecord($reason: String!, $userID: ID) {
+ createCheatRecord(reason: $reason, userID: $userID) {
+ id
+ cheatedAt
+ reason
+ user {
+ id
+ email
+ name
+ }
+ }
+ }
+`);
+
+export const RESOLVE_CHEAT_RECORD_MUTATION = graphql(`
+ mutation ResolveCheatRecord($cheatRecordID: ID!, $reason: String!) {
+ resolveCheatRecord(cheatRecordID: $cheatRecordID, reason: $reason)
+ }
+`);
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/query.ts b/app/(admin)/(activity-management)/cheat-records/_components/query.ts
new file mode 100644
index 0000000..927acfd
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/query.ts
@@ -0,0 +1,60 @@
+import { graphql } from "@/gql";
+
+export const CHEAT_RECORD_BY_ID_QUERY = graphql(`
+ query CheatRecordById($id: ID!) {
+ cheatRecord(id: $id) {
+ id
+ cheatedAt
+ reason
+ resolvedAt
+ resolvedReason
+ user {
+ id
+ avatar
+ email
+ name
+ }
+ }
+ }
+`);
+
+export const CHEAT_RECORDS_TABLE_QUERY = graphql(`
+ query CheatRecordsTable(
+ $after: Cursor
+ $before: Cursor
+ $first: Int
+ $last: Int
+ $where: CheatRecordWhereInput
+ ) {
+ cheatRecords(
+ after: $after
+ before: $before
+ first: $first
+ last: $last
+ where: $where
+ ) {
+ totalCount
+ edges {
+ node {
+ id
+ cheatedAt
+ reason
+ resolvedAt
+ resolvedReason
+ user {
+ id
+ avatar
+ email
+ name
+ }
+ }
+ }
+ pageInfo {
+ endCursor
+ hasNextPage
+ hasPreviousPage
+ startCursor
+ }
+ }
+ }
+`);
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/resolve-form.tsx b/app/(admin)/(activity-management)/cheat-records/_components/resolve-form.tsx
new file mode 100644
index 0000000..34343ee
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/resolve-form.tsx
@@ -0,0 +1,68 @@
+"use client";
+
+import { Button } from "@/components/ui/button";
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
+import { Textarea } from "@/components/ui/textarea";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useEffect } from "react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+const resolveCheatRecordFormSchema = z.object({
+ reason: z.string().min(1, "請輸入解決原因"),
+});
+
+export type ResolveCheatRecordFormData = z.infer<
+ typeof resolveCheatRecordFormSchema
+>;
+
+export function ResolveCheatRecordForm({
+ defaultValues,
+ onSubmit,
+ onFormStateChange,
+}: {
+ defaultValues: ResolveCheatRecordFormData;
+ onSubmit: (data: ResolveCheatRecordFormData) => void;
+ onFormStateChange?: (isDirty: boolean) => void;
+}) {
+ const form = useForm({
+ resolver: zodResolver(resolveCheatRecordFormSchema),
+ defaultValues,
+ });
+
+ const isDirty = form.formState.isDirty;
+
+ useEffect(() => {
+ onFormStateChange?.(isDirty);
+ }, [isDirty, onFormStateChange]);
+
+ return (
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/_components/resolve.tsx b/app/(admin)/(activity-management)/cheat-records/_components/resolve.tsx
new file mode 100644
index 0000000..d93a917
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/_components/resolve.tsx
@@ -0,0 +1,216 @@
+"use client";
+
+import { buttonVariants } from "@/components/ui/button";
+import { ConfirmationDialog } from "@/components/ui/confirmation-dialog";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
+import { useDialogCloseConfirmation } from "@/hooks/use-dialog-close-confirmation";
+import { skipToken, useMutation, useSuspenseQuery } from "@apollo/client/react";
+import { CheckCircle2 } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { Suspense, useState } from "react";
+import { toast } from "sonner";
+import { RESOLVE_CHEAT_RECORD_MUTATION } from "./mutation";
+import { CHEAT_RECORD_BY_ID_QUERY, CHEAT_RECORDS_TABLE_QUERY } from "./query";
+import { ResolveCheatRecordForm, type ResolveCheatRecordFormData } from "./resolve-form";
+
+export function ResolveCheatRecordDropdownTrigger({ id }: { id: string }) {
+ const router = useRouter();
+ const [open, setOpen] = useState(false);
+ const [isFormDirty, setIsFormDirty] = useState(false);
+
+ const {
+ showConfirmation,
+ handleDialogOpenChange,
+ handleConfirmClose,
+ handleCancelClose,
+ } = useDialogCloseConfirmation({
+ isDirty: isFormDirty,
+ setOpen,
+ onConfirmedClose: () => {
+ setIsFormDirty(false);
+ },
+ });
+
+ const handleFormStateChange = (isDirty: boolean) => {
+ setIsFormDirty(isDirty);
+ };
+
+ const handleCompleted = () => {
+ setIsFormDirty(false);
+ setOpen(false);
+ router.refresh();
+ };
+
+ return (
+ <>
+
+
+ {}}
+ onConfirm={handleConfirmClose}
+ onCancel={handleCancelClose}
+ />
+ >
+ );
+}
+
+export function ResolveCheatRecordButtonTrigger({ id }: { id: string }) {
+ const router = useRouter();
+ const [open, setOpen] = useState(false);
+ const [isFormDirty, setIsFormDirty] = useState(false);
+
+ const {
+ showConfirmation,
+ handleDialogOpenChange,
+ handleConfirmClose,
+ handleCancelClose,
+ } = useDialogCloseConfirmation({
+ isDirty: isFormDirty,
+ setOpen,
+ onConfirmedClose: () => {
+ setIsFormDirty(false);
+ },
+ });
+
+ const handleFormStateChange = (isDirty: boolean) => {
+ setIsFormDirty(isDirty);
+ };
+
+ const handleCompleted = () => {
+ setIsFormDirty(false);
+ setOpen(false);
+ router.refresh();
+ };
+
+ return (
+ <>
+
+
+ {}}
+ onConfirm={handleConfirmClose}
+ onCancel={handleCancelClose}
+ />
+ >
+ );
+}
+
+function ResolveCheatRecordDialogContent({
+ id,
+ open,
+ onCompleted,
+ onFormStateChange,
+}: {
+ id: string;
+ open: boolean;
+ onCompleted: () => void;
+ onFormStateChange: (isDirty: boolean) => void;
+}) {
+ const { data: cheatRecord } = useSuspenseQuery(
+ CHEAT_RECORD_BY_ID_QUERY,
+ open
+ ? {
+ variables: { id },
+ }
+ : skipToken,
+ );
+
+ const [resolveCheatRecord] = useMutation(RESOLVE_CHEAT_RECORD_MUTATION, {
+ refetchQueries: [
+ { query: CHEAT_RECORDS_TABLE_QUERY },
+ { query: CHEAT_RECORD_BY_ID_QUERY, variables: { id } },
+ ],
+ awaitRefetchQueries: true,
+
+ onError(error) {
+ toast.error("解決記錄失敗", {
+ description: error.message,
+ });
+ },
+
+ onCompleted() {
+ toast.success("記錄已解決");
+ onCompleted();
+ },
+ });
+
+ const onSubmit = (data: ResolveCheatRecordFormData) => {
+ try {
+ resolveCheatRecord({
+ variables: {
+ cheatRecordID: id,
+ reason: data.reason,
+ },
+ });
+ } catch (error) {
+ toast.error("解決記錄失敗", {
+ description: error instanceof Error ? error.message : "未知錯誤",
+ });
+ }
+ };
+
+ if (!cheatRecord?.cheatRecord) {
+ return null;
+ }
+
+ return (
+
+
+ 解決作弊記錄
+
+ 為此作弊記錄標記為已解決,並提供解決原因。
+
+
+
+
+ );
+}
diff --git a/app/(admin)/(activity-management)/cheat-records/page.tsx b/app/(admin)/(activity-management)/cheat-records/page.tsx
new file mode 100644
index 0000000..45e6759
--- /dev/null
+++ b/app/(admin)/(activity-management)/cheat-records/page.tsx
@@ -0,0 +1,33 @@
+import { SiteHeader } from "@/components/site-header";
+import type { Metadata } from "next";
+import { CreateCheatRecordButtonTrigger } from "./_components/create";
+import FilterableDataTable from "./_components/filterable-data-table";
+
+export const metadata: Metadata = {
+ title: "作弊記錄",
+};
+
+export default function Page() {
+ return (
+ <>
+
+
+
+
+
作弊記錄管理
+
查看和管理所有作弊記錄。
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/app/(admin)/(user-management)/users/[id]/_components/cheat-records.tsx b/app/(admin)/(user-management)/users/[id]/_components/cheat-records.tsx
new file mode 100644
index 0000000..c0abec7
--- /dev/null
+++ b/app/(admin)/(user-management)/users/[id]/_components/cheat-records.tsx
@@ -0,0 +1,117 @@
+"use client";
+
+import { CardLayout } from "@/components/card-layout";
+import { Badge } from "@/components/ui/badge";
+import { StyledLink } from "@/components/ui/link";
+import { type FragmentType, graphql, useFragment } from "@/gql";
+import { AlertTriangle } from "lucide-react";
+
+const USER_CHEAT_RECORDS_CARD_FRAGMENT = graphql(`
+ fragment UserCheatRecordsCard on User {
+ id
+ cheating
+ cheatRecords(first: 5, where: { resolvedAtIsNil: true }) {
+ totalCount
+ edges {
+ node {
+ ...UserCheatRecordLine
+ id
+ }
+ }
+ }
+ }
+`);
+
+const USER_CHEAT_RECORD_LINE_FRAGMENT = graphql(`
+ fragment UserCheatRecordLine on CheatRecord {
+ id
+ cheatedAt
+ reason
+ }
+`);
+
+export function CheatRecordsCard({
+ fragment,
+}: {
+ fragment: FragmentType;
+}) {
+ const { id, cheating, cheatRecords } = useFragment(
+ USER_CHEAT_RECORDS_CARD_FRAGMENT,
+ fragment,
+ );
+
+ const unresolvedRecords = cheatRecords?.edges
+ ?.map((edge) => edge?.node)
+ .filter((node) => node !== null && node !== undefined) ?? [];
+
+ const totalUnresolved = cheatRecords?.totalCount ?? 0;
+
+ return (
+
+
+
+
+
+
{totalUnresolved}
+
+ {cheating ? "未解決記錄" : "無未解決記錄"}
+
+
+
+
+ {unresolvedRecords.length > 0 && (
+
+
最近未解決記錄
+
+ {unresolvedRecords.map((record) => {
+ if (!record) return null;
+ return ;
+ })}
+
+ {totalUnresolved > unresolvedRecords.length && (
+
+
+ 查看全部記錄 ({totalUnresolved})
+
+
+ )}
+
+ )}
+
+ {unresolvedRecords.length === 0 && totalUnresolved === 0 && (
+
暫無作弊記錄
+ )}
+
+
+ );
+}
+
+function CheatRecordLine({
+ fragment,
+}: {
+ fragment: FragmentType;
+}) {
+ const { id, reason, cheatedAt } = useFragment(
+ USER_CHEAT_RECORD_LINE_FRAGMENT,
+ fragment,
+ );
+
+ return (
+
+
+
+ {reason}
+
+
+ {new Date(cheatedAt).toLocaleString("zh-TW", {
+ timeZone: "Asia/Taipei",
+ })}
+
+
+
未解決
+
+ );
+}
diff --git a/app/(admin)/(user-management)/users/[id]/_components/user-cards.tsx b/app/(admin)/(user-management)/users/[id]/_components/user-cards.tsx
index 300da82..1c3f2eb 100644
--- a/app/(admin)/(user-management)/users/[id]/_components/user-cards.tsx
+++ b/app/(admin)/(user-management)/users/[id]/_components/user-cards.tsx
@@ -3,6 +3,7 @@
import { graphql } from "@/gql";
import { useSuspenseQuery } from "@apollo/client/react";
import { AuditInfoCard } from "./audit-info";
+import { CheatRecordsCard } from "./cheat-records";
import { GroupsCard } from "./groups";
import { PointsCard } from "./points";
import { QuestionsCard } from "./questions";
@@ -11,6 +12,7 @@ const USER_CARDS_QUERY = graphql(`
query UserCards($id: ID!) {
user(id: $id) {
...UserAuditInfoCard
+ ...UserCheatRecordsCard
...UserGroupsCard
...UserPointsCard
...UserQuestionsCard
@@ -37,6 +39,7 @@ export function UserCards({ id }: { id: string }) {
+
);
}
diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx
index 38c9701..6803f18 100644
--- a/components/app-sidebar.tsx
+++ b/components/app-sidebar.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Activity, Book, Code, Coins, LibrarySquare, type LucideIcon, Send, SquareUser } from "lucide-react";
+import { Activity, Ban, Book, Code, Coins, LibrarySquare, type LucideIcon, Send, SquareUser } from "lucide-react";
import * as React from "react";
import { NavMain } from "@/components/nav-main";
@@ -122,6 +122,12 @@ const buildNavbar = (
icon: Coins,
isActive: pathname.startsWith("/points"),
},
+ {
+ title: "作弊記錄",
+ url: "/cheat-records",
+ icon: Ban,
+ isActive: pathname.startsWith("/cheat-records"),
+ },
],
},
],
diff --git a/gql/gql.ts b/gql/gql.ts
index bac796d..6f9aa35 100644
--- a/gql/gql.ts
+++ b/gql/gql.ts
@@ -14,6 +14,15 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
*/
type Documents = {
+ "\n query CheatRecordDetails($id: ID!) {\n cheatRecord(id: $id) {\n ...CheatRecordDetailsCard\n id\n }\n }\n": typeof types.CheatRecordDetailsDocument,
+ "\n fragment CheatRecordDetailsCard on CheatRecord {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n": typeof types.CheatRecordDetailsCardFragmentDoc,
+ "\n query CheatRecordHeader($id: ID!) {\n cheatRecord(id: $id) {\n id\n reason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n": typeof types.CheatRecordHeaderDocument,
+ "\n query CheatRecordResolveButton($id: ID!) {\n cheatRecord(id: $id) {\n id\n resolvedAt\n }\n }\n": typeof types.CheatRecordResolveButtonDocument,
+ "\n query CreateCheatRecordFormUserInfo($id: ID!) {\n user(id: $id) {\n id\n email\n name\n }\n }\n": typeof types.CreateCheatRecordFormUserInfoDocument,
+ "\n mutation CreateCheatRecord($reason: String!, $userID: ID) {\n createCheatRecord(reason: $reason, userID: $userID) {\n id\n cheatedAt\n reason\n user {\n id\n email\n name\n }\n }\n }\n": typeof types.CreateCheatRecordDocument,
+ "\n mutation ResolveCheatRecord($cheatRecordID: ID!, $reason: String!) {\n resolveCheatRecord(cheatRecordID: $cheatRecordID, reason: $reason)\n }\n": typeof types.ResolveCheatRecordDocument,
+ "\n query CheatRecordById($id: ID!) {\n cheatRecord(id: $id) {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n": typeof types.CheatRecordByIdDocument,
+ "\n query CheatRecordsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: CheatRecordWhereInput\n ) {\n cheatRecords(\n after: $after\n before: $before\n first: $first\n last: $last\n where: $where\n ) {\n totalCount\n edges {\n node {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n": typeof types.CheatRecordsTableDocument,
"\n query EventById($id: ID!) {\n event(id: $id) {\n id\n payload\n triggeredAt\n type\n user {\n id\n name\n }\n }\n }\n": typeof types.EventByIdDocument,
"\n query EventsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: EventWhereInput\n ) {\n events(after: $after, before: $before, first: $first, last: $last, orderBy: { field: TRIGGERED_AT, direction: DESC }, where: $where) {\n totalCount\n edges {\n node {\n id\n triggeredAt\n type\n user {\n id\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n": typeof types.EventsTableDocument,
"\n query PointHeader($id: ID!) {\n pointGrant(id: $id) {\n id\n grantedAt\n points\n }\n }\n": typeof types.PointHeaderDocument,
@@ -77,12 +86,14 @@ type Documents = {
"\n query ScopeSetTable {\n scopeSets {\n id\n description\n scopes\n slug\n }\n }\n": typeof types.ScopeSetTableDocument,
"\n query ScopeSetById($id: ID!) {\n scopeSet(filter: { id: $id }) {\n id\n description\n scopes\n slug\n }\n }\n": typeof types.ScopeSetByIdDocument,
"\n fragment UserAuditInfoCard on User {\n id\n createdAt\n updatedAt\n }\n": typeof types.UserAuditInfoCardFragmentDoc,
+ "\n fragment UserCheatRecordsCard on User {\n id\n cheating\n cheatRecords(first: 5, where: { resolvedAtIsNil: true }) {\n totalCount\n edges {\n node {\n ...UserCheatRecordLine\n id\n }\n }\n }\n }\n": typeof types.UserCheatRecordsCardFragmentDoc,
+ "\n fragment UserCheatRecordLine on CheatRecord {\n id\n cheatedAt\n reason\n }\n": typeof types.UserCheatRecordLineFragmentDoc,
"\n fragment UserGroupsCard on User {\n id\n group {\n id\n name\n }\n }\n": typeof types.UserGroupsCardFragmentDoc,
"\n query UserHeader($id: ID!) {\n user(id: $id) {\n id\n avatar\n email\n name\n }\n }\n": typeof types.UserHeaderDocument,
"\n fragment UserPointsCard on User {\n id\n totalPoints\n\n points(first: 5, orderBy: { field: GRANTED_AT, direction: DESC }) {\n edges {\n node {\n ...UserPointHistoryLine\n id\n }\n }\n }\n }\n": typeof types.UserPointsCardFragmentDoc,
"\n fragment UserPointHistoryLine on Point {\n id\n description\n grantedAt\n points\n }\n": typeof types.UserPointHistoryLineFragmentDoc,
"\n fragment UserQuestionsCard on User {\n id\n submissionStatistics {\n attemptedQuestions\n solvedQuestions\n totalQuestions\n\n solvedQuestionByDifficulty {\n difficulty\n solvedQuestions\n }\n }\n }\n": typeof types.UserQuestionsCardFragmentDoc,
- "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n": typeof types.UserCardsDocument,
+ "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserCheatRecordsCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n": typeof types.UserCardsDocument,
"\n mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {\n updateUser(id: $id, input: $input) {\n id\n }\n }\n": typeof types.UpdateUserDocument,
"\n mutation DeleteUser($id: ID!) {\n deleteUser(id: $id)\n }\n": typeof types.DeleteUserDocument,
"\n mutation LogoutUserDevices($userID: ID!) {\n logoutUser(userID: $userID)\n }\n": typeof types.LogoutUserDevicesDocument,
@@ -102,6 +113,15 @@ type Documents = {
"\n query BasicUserInfo {\n me {\n id\n avatar\n email\n name\n\n group {\n id\n name\n }\n }\n }\n": typeof types.BasicUserInfoDocument,
};
const documents: Documents = {
+ "\n query CheatRecordDetails($id: ID!) {\n cheatRecord(id: $id) {\n ...CheatRecordDetailsCard\n id\n }\n }\n": types.CheatRecordDetailsDocument,
+ "\n fragment CheatRecordDetailsCard on CheatRecord {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n": types.CheatRecordDetailsCardFragmentDoc,
+ "\n query CheatRecordHeader($id: ID!) {\n cheatRecord(id: $id) {\n id\n reason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n": types.CheatRecordHeaderDocument,
+ "\n query CheatRecordResolveButton($id: ID!) {\n cheatRecord(id: $id) {\n id\n resolvedAt\n }\n }\n": types.CheatRecordResolveButtonDocument,
+ "\n query CreateCheatRecordFormUserInfo($id: ID!) {\n user(id: $id) {\n id\n email\n name\n }\n }\n": types.CreateCheatRecordFormUserInfoDocument,
+ "\n mutation CreateCheatRecord($reason: String!, $userID: ID) {\n createCheatRecord(reason: $reason, userID: $userID) {\n id\n cheatedAt\n reason\n user {\n id\n email\n name\n }\n }\n }\n": types.CreateCheatRecordDocument,
+ "\n mutation ResolveCheatRecord($cheatRecordID: ID!, $reason: String!) {\n resolveCheatRecord(cheatRecordID: $cheatRecordID, reason: $reason)\n }\n": types.ResolveCheatRecordDocument,
+ "\n query CheatRecordById($id: ID!) {\n cheatRecord(id: $id) {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n": types.CheatRecordByIdDocument,
+ "\n query CheatRecordsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: CheatRecordWhereInput\n ) {\n cheatRecords(\n after: $after\n before: $before\n first: $first\n last: $last\n where: $where\n ) {\n totalCount\n edges {\n node {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n": types.CheatRecordsTableDocument,
"\n query EventById($id: ID!) {\n event(id: $id) {\n id\n payload\n triggeredAt\n type\n user {\n id\n name\n }\n }\n }\n": types.EventByIdDocument,
"\n query EventsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: EventWhereInput\n ) {\n events(after: $after, before: $before, first: $first, last: $last, orderBy: { field: TRIGGERED_AT, direction: DESC }, where: $where) {\n totalCount\n edges {\n node {\n id\n triggeredAt\n type\n user {\n id\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n": types.EventsTableDocument,
"\n query PointHeader($id: ID!) {\n pointGrant(id: $id) {\n id\n grantedAt\n points\n }\n }\n": types.PointHeaderDocument,
@@ -165,12 +185,14 @@ const documents: Documents = {
"\n query ScopeSetTable {\n scopeSets {\n id\n description\n scopes\n slug\n }\n }\n": types.ScopeSetTableDocument,
"\n query ScopeSetById($id: ID!) {\n scopeSet(filter: { id: $id }) {\n id\n description\n scopes\n slug\n }\n }\n": types.ScopeSetByIdDocument,
"\n fragment UserAuditInfoCard on User {\n id\n createdAt\n updatedAt\n }\n": types.UserAuditInfoCardFragmentDoc,
+ "\n fragment UserCheatRecordsCard on User {\n id\n cheating\n cheatRecords(first: 5, where: { resolvedAtIsNil: true }) {\n totalCount\n edges {\n node {\n ...UserCheatRecordLine\n id\n }\n }\n }\n }\n": types.UserCheatRecordsCardFragmentDoc,
+ "\n fragment UserCheatRecordLine on CheatRecord {\n id\n cheatedAt\n reason\n }\n": types.UserCheatRecordLineFragmentDoc,
"\n fragment UserGroupsCard on User {\n id\n group {\n id\n name\n }\n }\n": types.UserGroupsCardFragmentDoc,
"\n query UserHeader($id: ID!) {\n user(id: $id) {\n id\n avatar\n email\n name\n }\n }\n": types.UserHeaderDocument,
"\n fragment UserPointsCard on User {\n id\n totalPoints\n\n points(first: 5, orderBy: { field: GRANTED_AT, direction: DESC }) {\n edges {\n node {\n ...UserPointHistoryLine\n id\n }\n }\n }\n }\n": types.UserPointsCardFragmentDoc,
"\n fragment UserPointHistoryLine on Point {\n id\n description\n grantedAt\n points\n }\n": types.UserPointHistoryLineFragmentDoc,
"\n fragment UserQuestionsCard on User {\n id\n submissionStatistics {\n attemptedQuestions\n solvedQuestions\n totalQuestions\n\n solvedQuestionByDifficulty {\n difficulty\n solvedQuestions\n }\n }\n }\n": types.UserQuestionsCardFragmentDoc,
- "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n": types.UserCardsDocument,
+ "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserCheatRecordsCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n": types.UserCardsDocument,
"\n mutation UpdateUser($id: ID!, $input: UpdateUserInput!) {\n updateUser(id: $id, input: $input) {\n id\n }\n }\n": types.UpdateUserDocument,
"\n mutation DeleteUser($id: ID!) {\n deleteUser(id: $id)\n }\n": types.DeleteUserDocument,
"\n mutation LogoutUserDevices($userID: ID!) {\n logoutUser(userID: $userID)\n }\n": types.LogoutUserDevicesDocument,
@@ -204,6 +226,42 @@ const documents: Documents = {
*/
export function graphql(source: string): unknown;
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CheatRecordDetails($id: ID!) {\n cheatRecord(id: $id) {\n ...CheatRecordDetailsCard\n id\n }\n }\n"): (typeof documents)["\n query CheatRecordDetails($id: ID!) {\n cheatRecord(id: $id) {\n ...CheatRecordDetailsCard\n id\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n fragment CheatRecordDetailsCard on CheatRecord {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n"): (typeof documents)["\n fragment CheatRecordDetailsCard on CheatRecord {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CheatRecordHeader($id: ID!) {\n cheatRecord(id: $id) {\n id\n reason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n"): (typeof documents)["\n query CheatRecordHeader($id: ID!) {\n cheatRecord(id: $id) {\n id\n reason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CheatRecordResolveButton($id: ID!) {\n cheatRecord(id: $id) {\n id\n resolvedAt\n }\n }\n"): (typeof documents)["\n query CheatRecordResolveButton($id: ID!) {\n cheatRecord(id: $id) {\n id\n resolvedAt\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CreateCheatRecordFormUserInfo($id: ID!) {\n user(id: $id) {\n id\n email\n name\n }\n }\n"): (typeof documents)["\n query CreateCheatRecordFormUserInfo($id: ID!) {\n user(id: $id) {\n id\n email\n name\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n mutation CreateCheatRecord($reason: String!, $userID: ID) {\n createCheatRecord(reason: $reason, userID: $userID) {\n id\n cheatedAt\n reason\n user {\n id\n email\n name\n }\n }\n }\n"): (typeof documents)["\n mutation CreateCheatRecord($reason: String!, $userID: ID) {\n createCheatRecord(reason: $reason, userID: $userID) {\n id\n cheatedAt\n reason\n user {\n id\n email\n name\n }\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n mutation ResolveCheatRecord($cheatRecordID: ID!, $reason: String!) {\n resolveCheatRecord(cheatRecordID: $cheatRecordID, reason: $reason)\n }\n"): (typeof documents)["\n mutation ResolveCheatRecord($cheatRecordID: ID!, $reason: String!) {\n resolveCheatRecord(cheatRecordID: $cheatRecordID, reason: $reason)\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CheatRecordById($id: ID!) {\n cheatRecord(id: $id) {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n"): (typeof documents)["\n query CheatRecordById($id: ID!) {\n cheatRecord(id: $id) {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n query CheatRecordsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: CheatRecordWhereInput\n ) {\n cheatRecords(\n after: $after\n before: $before\n first: $first\n last: $last\n where: $where\n ) {\n totalCount\n edges {\n node {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n"): (typeof documents)["\n query CheatRecordsTable(\n $after: Cursor\n $before: Cursor\n $first: Int\n $last: Int\n $where: CheatRecordWhereInput\n ) {\n cheatRecords(\n after: $after\n before: $before\n first: $first\n last: $last\n where: $where\n ) {\n totalCount\n edges {\n node {\n id\n cheatedAt\n reason\n resolvedAt\n resolvedReason\n user {\n id\n avatar\n email\n name\n }\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -456,6 +514,14 @@ export function graphql(source: "\n query ScopeSetById($id: ID!) {\n scopeSe
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment UserAuditInfoCard on User {\n id\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment UserAuditInfoCard on User {\n id\n createdAt\n updatedAt\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n fragment UserCheatRecordsCard on User {\n id\n cheating\n cheatRecords(first: 5, where: { resolvedAtIsNil: true }) {\n totalCount\n edges {\n node {\n ...UserCheatRecordLine\n id\n }\n }\n }\n }\n"): (typeof documents)["\n fragment UserCheatRecordsCard on User {\n id\n cheating\n cheatRecords(first: 5, where: { resolvedAtIsNil: true }) {\n totalCount\n edges {\n node {\n ...UserCheatRecordLine\n id\n }\n }\n }\n }\n"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "\n fragment UserCheatRecordLine on CheatRecord {\n id\n cheatedAt\n reason\n }\n"): (typeof documents)["\n fragment UserCheatRecordLine on CheatRecord {\n id\n cheatedAt\n reason\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -479,7 +545,7 @@ export function graphql(source: "\n fragment UserQuestionsCard on User {\n i
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
-export function graphql(source: "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n"): (typeof documents)["\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n"];
+export function graphql(source: "\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserCheatRecordsCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n"): (typeof documents)["\n query UserCards($id: ID!) {\n user(id: $id) {\n ...UserAuditInfoCard\n ...UserCheatRecordsCard\n ...UserGroupsCard\n ...UserPointsCard\n ...UserQuestionsCard\n id\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
diff --git a/gql/graphql.ts b/gql/graphql.ts
index 554e9b2..9cfeae1 100644
--- a/gql/graphql.ts
+++ b/gql/graphql.ts
@@ -25,6 +25,108 @@ export type Scalars = {
Time: { input: string; output: string; }
};
+export type CheatRecord = Node & {
+ __typename?: 'CheatRecord';
+ cheatedAt: Scalars['Time']['output'];
+ id: Scalars['ID']['output'];
+ reason: Scalars['String']['output'];
+ resolvedAt?: Maybe;
+ resolvedReason?: Maybe;
+ user: User;
+};
+
+/** A connection to a list of items. */
+export type CheatRecordConnection = {
+ __typename?: 'CheatRecordConnection';
+ /** A list of edges. */
+ edges?: Maybe>>;
+ /** Information to aid in pagination. */
+ pageInfo: PageInfo;
+ /** Identifies the total count of items in the connection. */
+ totalCount: Scalars['Int']['output'];
+};
+
+/** An edge in a connection. */
+export type CheatRecordEdge = {
+ __typename?: 'CheatRecordEdge';
+ /** A cursor for use in pagination. */
+ cursor: Scalars['Cursor']['output'];
+ /** The item at the end of the edge. */
+ node?: Maybe;
+};
+
+/**
+ * CheatRecordWhereInput is used for filtering CheatRecord objects.
+ * Input was generated by ent.
+ */
+export type CheatRecordWhereInput = {
+ and?: InputMaybe>;
+ /** cheated_at field predicates */
+ cheatedAt?: InputMaybe;
+ cheatedAtGT?: InputMaybe;
+ cheatedAtGTE?: InputMaybe;
+ cheatedAtIn?: InputMaybe>;
+ cheatedAtLT?: InputMaybe;
+ cheatedAtLTE?: InputMaybe;
+ cheatedAtNEQ?: InputMaybe;
+ cheatedAtNotIn?: InputMaybe>;
+ /** user edge predicates */
+ hasUser?: InputMaybe;
+ hasUserWith?: InputMaybe>;
+ /** id field predicates */
+ id?: InputMaybe;
+ idGT?: InputMaybe;
+ idGTE?: InputMaybe;
+ idIn?: InputMaybe>;
+ idLT?: InputMaybe;
+ idLTE?: InputMaybe;
+ idNEQ?: InputMaybe;
+ idNotIn?: InputMaybe>;
+ not?: InputMaybe;
+ or?: InputMaybe>;
+ /** reason field predicates */
+ reason?: InputMaybe;
+ reasonContains?: InputMaybe;
+ reasonContainsFold?: InputMaybe;
+ reasonEqualFold?: InputMaybe;
+ reasonGT?: InputMaybe;
+ reasonGTE?: InputMaybe;
+ reasonHasPrefix?: InputMaybe;
+ reasonHasSuffix?: InputMaybe;
+ reasonIn?: InputMaybe>;
+ reasonLT?: InputMaybe;
+ reasonLTE?: InputMaybe;
+ reasonNEQ?: InputMaybe;
+ reasonNotIn?: InputMaybe>;
+ /** resolved_at field predicates */
+ resolvedAt?: InputMaybe;
+ resolvedAtGT?: InputMaybe;
+ resolvedAtGTE?: InputMaybe;
+ resolvedAtIn?: InputMaybe>;
+ resolvedAtIsNil?: InputMaybe;
+ resolvedAtLT?: InputMaybe;
+ resolvedAtLTE?: InputMaybe;
+ resolvedAtNEQ?: InputMaybe;
+ resolvedAtNotIn?: InputMaybe>;
+ resolvedAtNotNil?: InputMaybe;
+ /** resolved_reason field predicates */
+ resolvedReason?: InputMaybe;
+ resolvedReasonContains?: InputMaybe;
+ resolvedReasonContainsFold?: InputMaybe;
+ resolvedReasonEqualFold?: InputMaybe;
+ resolvedReasonGT?: InputMaybe;
+ resolvedReasonGTE?: InputMaybe;
+ resolvedReasonHasPrefix?: InputMaybe;
+ resolvedReasonHasSuffix?: InputMaybe;
+ resolvedReasonIn?: InputMaybe>;
+ resolvedReasonIsNil?: InputMaybe;
+ resolvedReasonLT?: InputMaybe;
+ resolvedReasonLTE?: InputMaybe;
+ resolvedReasonNEQ?: InputMaybe;
+ resolvedReasonNotIn?: InputMaybe>;
+ resolvedReasonNotNil?: InputMaybe;
+};
+
/**
* CreateDatabaseInput is used for create Database object.
* Input was generated by ent.
@@ -98,6 +200,7 @@ export type CreateScopeSetInput = {
*/
export type CreateUserInput = {
avatar?: InputMaybe;
+ cheatRecordIDs?: InputMaybe>;
email: Scalars['String']['input'];
eventIDs?: InputMaybe>;
groupID: Scalars['ID']['input'];
@@ -397,6 +500,15 @@ export type GroupWhereInput = {
export type Mutation = {
__typename?: 'Mutation';
+ /**
+ * Create a new cheat record for a user.
+ *
+ * If userID is not provided, the current user will be used.
+ * For this case, you should have "me:write" scope.
+ *
+ * If userID is provided, you should have "cheat_record:write" scope.
+ */
+ createCheatRecord: CheatRecord;
/** Create a database. */
createDatabase: Database;
/** Create a new group. */
@@ -428,6 +540,8 @@ export type Mutation = {
logoutAll: Scalars['Boolean']['output'];
/** Logout a user from all his devices. */
logoutUser: Scalars['Boolean']['output'];
+ /** Resolve a cheat record. */
+ resolveCheatRecord: Scalars['Boolean']['output'];
/** Submit your answer to a question. */
submitAnswer: SubmissionResult;
/** Update a database. */
@@ -445,6 +559,12 @@ export type Mutation = {
};
+export type MutationCreateCheatRecordArgs = {
+ reason: Scalars['String']['input'];
+ userID?: InputMaybe;
+};
+
+
export type MutationCreateDatabaseArgs = {
input: CreateDatabaseInput;
};
@@ -505,6 +625,12 @@ export type MutationLogoutUserArgs = {
};
+export type MutationResolveCheatRecordArgs = {
+ cheatRecordID: Scalars['ID']['input'];
+ reason: Scalars['String']['input'];
+};
+
+
export type MutationSubmitAnswerArgs = {
answer: Scalars['String']['input'];
id: Scalars['ID']['input'];
@@ -678,6 +804,9 @@ export type PointWhereInput = {
export type Query = {
__typename?: 'Query';
+ /** Get a cheat record by ID. */
+ cheatRecord: CheatRecord;
+ cheatRecords: CheatRecordConnection;
/** Get a database by ID. */
database: Database;
databases: Array;
@@ -729,6 +858,20 @@ export type Query = {
};
+export type QueryCheatRecordArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+export type QueryCheatRecordsArgs = {
+ after?: InputMaybe;
+ before?: InputMaybe;
+ first?: InputMaybe;
+ last?: InputMaybe;
+ where?: InputMaybe;
+};
+
+
export type QueryDatabaseArgs = {
id: Scalars['ID']['input'];
};
@@ -1358,16 +1501,19 @@ export type UpdateScopeSetInput = {
* Input was generated by ent.
*/
export type UpdateUserInput = {
+ addCheatRecordIDs?: InputMaybe>;
addEventIDs?: InputMaybe>;
addPointIDs?: InputMaybe>;
addSubmissionIDs?: InputMaybe>;
avatar?: InputMaybe;
clearAvatar?: InputMaybe;
+ clearCheatRecords?: InputMaybe;
clearEvents?: InputMaybe;
clearPoints?: InputMaybe;
clearSubmissions?: InputMaybe;
groupID?: InputMaybe;
name?: InputMaybe;
+ removeCheatRecordIDs?: InputMaybe>;
removeEventIDs?: InputMaybe>;
removePointIDs?: InputMaybe>;
removeSubmissionIDs?: InputMaybe>;
@@ -1376,6 +1522,9 @@ export type UpdateUserInput = {
export type User = Node & {
__typename?: 'User';
avatar?: Maybe;
+ cheatRecords: CheatRecordConnection;
+ /** Does this user have any existing unresolved cheat records? */
+ cheating: Scalars['Boolean']['output'];
createdAt: Scalars['Time']['output'];
deletedAt?: Maybe;
email: Scalars['String']['output'];
@@ -1394,6 +1543,15 @@ export type User = Node & {
};
+export type UserCheatRecordsArgs = {
+ after?: InputMaybe;
+ before?: InputMaybe;
+ first?: InputMaybe;
+ last?: InputMaybe;
+ where?: InputMaybe;
+};
+
+
export type UserEventsArgs = {
after?: InputMaybe;
before?: InputMaybe;
@@ -1519,6 +1677,9 @@ export type UserWhereInput = {
emailLTE?: InputMaybe;
emailNEQ?: InputMaybe;
emailNotIn?: InputMaybe>;
+ /** cheat_records edge predicates */
+ hasCheatRecords?: InputMaybe;
+ hasCheatRecordsWith?: InputMaybe>;
/** events edge predicates */
hasEvents?: InputMaybe;
hasEventsWith?: InputMaybe>;
@@ -1567,6 +1728,73 @@ export type UserWhereInput = {
updatedAtNotIn?: InputMaybe>;
};
+export type CheatRecordDetailsQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type CheatRecordDetailsQuery = { __typename?: 'Query', cheatRecord: (
+ { __typename?: 'CheatRecord', id: string }
+ & { ' $fragmentRefs'?: { 'CheatRecordDetailsCardFragment': CheatRecordDetailsCardFragment } }
+ ) };
+
+export type CheatRecordDetailsCardFragment = { __typename?: 'CheatRecord', id: string, cheatedAt: string, reason: string, resolvedAt?: string | null, resolvedReason?: string | null, user: { __typename?: 'User', id: string, avatar?: string | null, email: string, name: string } } & { ' $fragmentName'?: 'CheatRecordDetailsCardFragment' };
+
+export type CheatRecordHeaderQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type CheatRecordHeaderQuery = { __typename?: 'Query', cheatRecord: { __typename?: 'CheatRecord', id: string, reason: string, user: { __typename?: 'User', id: string, avatar?: string | null, email: string, name: string } } };
+
+export type CheatRecordResolveButtonQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type CheatRecordResolveButtonQuery = { __typename?: 'Query', cheatRecord: { __typename?: 'CheatRecord', id: string, resolvedAt?: string | null } };
+
+export type CreateCheatRecordFormUserInfoQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type CreateCheatRecordFormUserInfoQuery = { __typename?: 'Query', user: { __typename?: 'User', id: string, email: string, name: string } };
+
+export type CreateCheatRecordMutationVariables = Exact<{
+ reason: Scalars['String']['input'];
+ userID?: InputMaybe;
+}>;
+
+
+export type CreateCheatRecordMutation = { __typename?: 'Mutation', createCheatRecord: { __typename?: 'CheatRecord', id: string, cheatedAt: string, reason: string, user: { __typename?: 'User', id: string, email: string, name: string } } };
+
+export type ResolveCheatRecordMutationVariables = Exact<{
+ cheatRecordID: Scalars['ID']['input'];
+ reason: Scalars['String']['input'];
+}>;
+
+
+export type ResolveCheatRecordMutation = { __typename?: 'Mutation', resolveCheatRecord: boolean };
+
+export type CheatRecordByIdQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type CheatRecordByIdQuery = { __typename?: 'Query', cheatRecord: { __typename?: 'CheatRecord', id: string, cheatedAt: string, reason: string, resolvedAt?: string | null, resolvedReason?: string | null, user: { __typename?: 'User', id: string, avatar?: string | null, email: string, name: string } } };
+
+export type CheatRecordsTableQueryVariables = Exact<{
+ after?: InputMaybe;
+ before?: InputMaybe;
+ first?: InputMaybe;
+ last?: InputMaybe;
+ where?: InputMaybe;
+}>;
+
+
+export type CheatRecordsTableQuery = { __typename?: 'Query', cheatRecords: { __typename?: 'CheatRecordConnection', totalCount: number, edges?: Array<{ __typename?: 'CheatRecordEdge', node?: { __typename?: 'CheatRecord', id: string, cheatedAt: string, reason: string, resolvedAt?: string | null, resolvedReason?: string | null, user: { __typename?: 'User', id: string, avatar?: string | null, email: string, name: string } } | null } | null> | null, pageInfo: { __typename?: 'PageInfo', endCursor?: any | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: any | null } } };
+
export type EventByIdQueryVariables = Exact<{
id: Scalars['ID']['input'];
}>;
@@ -1950,6 +2178,13 @@ export type ScopeSetByIdQuery = { __typename?: 'Query', scopeSet: { __typename?:
export type UserAuditInfoCardFragment = { __typename?: 'User', id: string, createdAt: string, updatedAt: string } & { ' $fragmentName'?: 'UserAuditInfoCardFragment' };
+export type UserCheatRecordsCardFragment = { __typename?: 'User', id: string, cheating: boolean, cheatRecords: { __typename?: 'CheatRecordConnection', totalCount: number, edges?: Array<{ __typename?: 'CheatRecordEdge', node?: (
+ { __typename?: 'CheatRecord', id: string }
+ & { ' $fragmentRefs'?: { 'UserCheatRecordLineFragment': UserCheatRecordLineFragment } }
+ ) | null } | null> | null } } & { ' $fragmentName'?: 'UserCheatRecordsCardFragment' };
+
+export type UserCheatRecordLineFragment = { __typename?: 'CheatRecord', id: string, cheatedAt: string, reason: string } & { ' $fragmentName'?: 'UserCheatRecordLineFragment' };
+
export type UserGroupsCardFragment = { __typename?: 'User', id: string, group: { __typename?: 'Group', id: string, name: string } } & { ' $fragmentName'?: 'UserGroupsCardFragment' };
export type UserHeaderQueryVariables = Exact<{
@@ -1975,7 +2210,7 @@ export type UserCardsQueryVariables = Exact<{
export type UserCardsQuery = { __typename?: 'Query', user: (
{ __typename?: 'User', id: string }
- & { ' $fragmentRefs'?: { 'UserAuditInfoCardFragment': UserAuditInfoCardFragment;'UserGroupsCardFragment': UserGroupsCardFragment;'UserPointsCardFragment': UserPointsCardFragment;'UserQuestionsCardFragment': UserQuestionsCardFragment } }
+ & { ' $fragmentRefs'?: { 'UserAuditInfoCardFragment': UserAuditInfoCardFragment;'UserCheatRecordsCardFragment': UserCheatRecordsCardFragment;'UserGroupsCardFragment': UserGroupsCardFragment;'UserPointsCardFragment': UserPointsCardFragment;'UserQuestionsCardFragment': UserQuestionsCardFragment } }
) };
export type UpdateUserMutationVariables = Exact<{
@@ -2084,6 +2319,7 @@ export type BasicUserInfoQueryVariables = Exact<{ [key: string]: never; }>;
export type BasicUserInfoQuery = { __typename?: 'Query', me: { __typename?: 'User', id: string, avatar?: string | null, email: string, name: string, group: { __typename?: 'Group', id: string, name: string } } };
+export const CheatRecordDetailsCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CheatRecordDetailsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedAt"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedReason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
export const PointDetailsCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PointDetailsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]} as unknown as DocumentNode;
export const PointUserCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PointUserCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
export const PointsTableRowFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PointsTableRow"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
@@ -2102,6 +2338,8 @@ export const GroupAuditInfoCardFragmentDoc = {"kind":"Document","definitions":[{
export const GroupScopeCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GroupScopeCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Group"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"scopeSets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"scopes"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]} as unknown as DocumentNode;
export const ScopeSetScopesCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScopeSetScopesCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ScopeSet"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"scopes"}}]}}]} as unknown as DocumentNode;
export const UserAuditInfoCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserAuditInfoCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode;
+export const UserCheatRecordLineFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCheatRecordLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}}]}}]} as unknown as DocumentNode;
+export const UserCheatRecordsCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCheatRecordsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheating"}},{"kind":"Field","name":{"kind":"Name","value":"cheatRecords"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"resolvedAtIsNil"},"value":{"kind":"BooleanValue","value":true}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserCheatRecordLine"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCheatRecordLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}}]}}]} as unknown as DocumentNode;
export const UserGroupsCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserGroupsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
export const UserPointHistoryLineFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointHistoryLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]} as unknown as DocumentNode;
export const UserPointsCardFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}},{"kind":"Field","name":{"kind":"Name","value":"points"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"field"},"value":{"kind":"EnumValue","value":"GRANTED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserPointHistoryLine"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointHistoryLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]} as unknown as DocumentNode;
@@ -2110,6 +2348,14 @@ export const ScoreDiffLineFragmentDoc = {"kind":"Document","definitions":[{"kind
export const UserCompletedQuestionsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCompletedQuestions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode;
export const UserTotalPointsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserTotalPoints"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}}]}}]}}]} as unknown as DocumentNode;
export const ScoreCellFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScoreCell"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ScoreDiffLine"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserCompletedQuestions"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserTotalPoints"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ScoreDiffLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"score"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCompletedQuestions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserTotalPoints"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RankingEdge"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}}]}}]}}]} as unknown as DocumentNode;
+export const CheatRecordDetailsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheatRecordDetails"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CheatRecordDetailsCard"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CheatRecordDetailsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedAt"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedReason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
+export const CheatRecordHeaderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheatRecordHeader"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const CheatRecordResolveButtonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheatRecordResolveButton"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedAt"}}]}}]}}]} as unknown as DocumentNode;
+export const CreateCheatRecordFormUserInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CreateCheatRecordFormUserInfo"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
+export const CreateCheatRecordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateCheatRecord"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"reason"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userID"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"reason"},"value":{"kind":"Variable","name":{"kind":"Name","value":"reason"}}},{"kind":"Argument","name":{"kind":"Name","value":"userID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const ResolveCheatRecordDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ResolveCheatRecord"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cheatRecordID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"reason"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resolveCheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cheatRecordID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cheatRecordID"}}},{"kind":"Argument","name":{"kind":"Name","value":"reason"},"value":{"kind":"Variable","name":{"kind":"Name","value":"reason"}}}]}]}}]} as unknown as DocumentNode;
+export const CheatRecordByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheatRecordById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cheatRecord"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedAt"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedReason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const CheatRecordsTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CheatRecordsTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Cursor"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"before"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Cursor"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"last"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecordWhereInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cheatRecords"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"before"},"value":{"kind":"Variable","name":{"kind":"Name","value":"before"}}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"Variable","name":{"kind":"Name","value":"last"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedAt"}},{"kind":"Field","name":{"kind":"Name","value":"resolvedReason"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}}]}}]}}]}}]} as unknown as DocumentNode;
export const EventByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EventById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"event"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"payload"}},{"kind":"Field","name":{"kind":"Name","value":"triggeredAt"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode;
export const EventsTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"EventsTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Cursor"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"before"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Cursor"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"last"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"EventWhereInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}},{"kind":"Argument","name":{"kind":"Name","value":"before"},"value":{"kind":"Variable","name":{"kind":"Name","value":"before"}}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"last"},"value":{"kind":"Variable","name":{"kind":"Name","value":"last"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"field"},"value":{"kind":"EnumValue","value":"TRIGGERED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"triggeredAt"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"endCursor"}},{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}}]}}]}}]}}]} as unknown as DocumentNode;
export const PointHeaderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PointHeader"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pointGrant"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]}}]} as unknown as DocumentNode;
@@ -2156,7 +2402,7 @@ export const DeleteScopeSetDocument = {"kind":"Document","definitions":[{"kind":
export const ScopeSetTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ScopeSetTable"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeSets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"scopes"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]} as unknown as DocumentNode;
export const ScopeSetByIdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ScopeSetById"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeSet"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"scopes"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]} as unknown as DocumentNode;
export const UserHeaderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserHeader"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode;
-export const UserCardsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserCards"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserAuditInfoCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserGroupsCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserPointsCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserQuestionsCard"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointHistoryLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserAuditInfoCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserGroupsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}},{"kind":"Field","name":{"kind":"Name","value":"points"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"field"},"value":{"kind":"EnumValue","value":"GRANTED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserPointHistoryLine"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserQuestionsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"attemptedQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"totalQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestionByDifficulty"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"difficulty"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode;
+export const UserCardsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserCards"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserAuditInfoCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserCheatRecordsCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserGroupsCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserPointsCard"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserQuestionsCard"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCheatRecordLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CheatRecord"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointHistoryLine"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Point"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"grantedAt"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserAuditInfoCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserCheatRecordsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cheating"}},{"kind":"Field","name":{"kind":"Name","value":"cheatRecords"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"resolvedAtIsNil"},"value":{"kind":"BooleanValue","value":true}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserCheatRecordLine"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserGroupsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"group"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserPointsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalPoints"}},{"kind":"Field","name":{"kind":"Name","value":"points"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"field"},"value":{"kind":"EnumValue","value":"GRANTED_AT"}},{"kind":"ObjectField","name":{"kind":"Name","value":"direction"},"value":{"kind":"EnumValue","value":"DESC"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserPointHistoryLine"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserQuestionsCard"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"submissionStatistics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"attemptedQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"totalQuestions"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestionByDifficulty"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"difficulty"}},{"kind":"Field","name":{"kind":"Name","value":"solvedQuestions"}}]}}]}}]}}]} as unknown as DocumentNode;
export const UpdateUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateUserInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode;
export const DeleteUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode;
export const LogoutUserDevicesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"LogoutUserDevices"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"userID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"logoutUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"userID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"userID"}}}]}]}}]} as unknown as DocumentNode;
diff --git a/schema.graphql b/schema.graphql
index d83f5bc..d2e7b69 100644
--- a/schema.graphql
+++ b/schema.graphql
@@ -19,6 +19,105 @@ directive @goModel(forceGenerate: Boolean, model: String, models: [String!]) on
directive @scope(scope: String!) on FIELD_DEFINITION
+type CheatRecord implements Node {
+ cheatedAt: Time!
+ id: ID!
+ reason: String!
+ resolvedAt: Time
+ resolvedReason: String
+ user: User!
+}
+
+"""A connection to a list of items."""
+type CheatRecordConnection {
+ """A list of edges."""
+ edges: [CheatRecordEdge]
+ """Information to aid in pagination."""
+ pageInfo: PageInfo!
+ """Identifies the total count of items in the connection."""
+ totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CheatRecordEdge {
+ """A cursor for use in pagination."""
+ cursor: Cursor!
+ """The item at the end of the edge."""
+ node: CheatRecord
+}
+
+"""
+CheatRecordWhereInput is used for filtering CheatRecord objects.
+Input was generated by ent.
+"""
+input CheatRecordWhereInput {
+ and: [CheatRecordWhereInput!]
+ """cheated_at field predicates"""
+ cheatedAt: Time
+ cheatedAtGT: Time
+ cheatedAtGTE: Time
+ cheatedAtIn: [Time!]
+ cheatedAtLT: Time
+ cheatedAtLTE: Time
+ cheatedAtNEQ: Time
+ cheatedAtNotIn: [Time!]
+ """user edge predicates"""
+ hasUser: Boolean
+ hasUserWith: [UserWhereInput!]
+ """id field predicates"""
+ id: ID
+ idGT: ID
+ idGTE: ID
+ idIn: [ID!]
+ idLT: ID
+ idLTE: ID
+ idNEQ: ID
+ idNotIn: [ID!]
+ not: CheatRecordWhereInput
+ or: [CheatRecordWhereInput!]
+ """reason field predicates"""
+ reason: String
+ reasonContains: String
+ reasonContainsFold: String
+ reasonEqualFold: String
+ reasonGT: String
+ reasonGTE: String
+ reasonHasPrefix: String
+ reasonHasSuffix: String
+ reasonIn: [String!]
+ reasonLT: String
+ reasonLTE: String
+ reasonNEQ: String
+ reasonNotIn: [String!]
+ """resolved_at field predicates"""
+ resolvedAt: Time
+ resolvedAtGT: Time
+ resolvedAtGTE: Time
+ resolvedAtIn: [Time!]
+ resolvedAtIsNil: Boolean
+ resolvedAtLT: Time
+ resolvedAtLTE: Time
+ resolvedAtNEQ: Time
+ resolvedAtNotIn: [Time!]
+ resolvedAtNotNil: Boolean
+ """resolved_reason field predicates"""
+ resolvedReason: String
+ resolvedReasonContains: String
+ resolvedReasonContainsFold: String
+ resolvedReasonEqualFold: String
+ resolvedReasonGT: String
+ resolvedReasonGTE: String
+ resolvedReasonHasPrefix: String
+ resolvedReasonHasSuffix: String
+ resolvedReasonIn: [String!]
+ resolvedReasonIsNil: Boolean
+ resolvedReasonLT: String
+ resolvedReasonLTE: String
+ resolvedReasonNEQ: String
+ resolvedReasonNotIn: [String!]
+ resolvedReasonNotNil: Boolean
+}
+
"""
CreateDatabaseInput is used for create Database object.
Input was generated by ent.
@@ -94,6 +193,7 @@ Input was generated by ent.
"""
input CreateUserInput {
avatar: String
+ cheatRecordIDs: [ID!]
email: String!
eventIDs: [ID!]
groupID: ID!
@@ -394,6 +494,15 @@ input GroupWhereInput {
scalar Map
type Mutation {
+ """
+ Create a new cheat record for a user.
+
+ If userID is not provided, the current user will be used.
+ For this case, you should have "me:write" scope.
+
+ If userID is provided, you should have "cheat_record:write" scope.
+ """
+ createCheatRecord(reason: String!, userID: ID): CheatRecord!
"""Create a database."""
createDatabase(input: CreateDatabaseInput!): Database!
"""Create a new group."""
@@ -425,6 +534,8 @@ type Mutation {
logoutAll: Boolean!
"""Logout a user from all his devices."""
logoutUser(userID: ID!): Boolean!
+ """Resolve a cheat record."""
+ resolveCheatRecord(cheatRecordID: ID!, reason: String!): Boolean!
"""Submit your answer to a question."""
submitAnswer(answer: String!, id: ID!): SubmissionResult!
"""Update a database."""
@@ -571,6 +682,22 @@ input PointWhereInput {
}
type Query {
+ """Get a cheat record by ID."""
+ cheatRecord(id: ID!): CheatRecord!
+ cheatRecords(
+ """Returns the elements in the list that come after the specified cursor."""
+ after: Cursor
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: Cursor
+ """Returns the first _n_ elements from the list."""
+ first: Int
+ """Returns the last _n_ elements from the list."""
+ last: Int
+ """Filtering options for CheatRecords returned from the connection."""
+ where: CheatRecordWhereInput
+ ): CheatRecordConnection!
"""Get a database by ID."""
database(id: ID!): Database!
databases: [Database!]!
@@ -1225,16 +1352,19 @@ UpdateUserInput is used for update User object.
Input was generated by ent.
"""
input UpdateUserInput {
+ addCheatRecordIDs: [ID!]
addEventIDs: [ID!]
addPointIDs: [ID!]
addSubmissionIDs: [ID!]
avatar: String
clearAvatar: Boolean
+ clearCheatRecords: Boolean
clearEvents: Boolean
clearPoints: Boolean
clearSubmissions: Boolean
groupID: ID
name: String
+ removeCheatRecordIDs: [ID!]
removeEventIDs: [ID!]
removePointIDs: [ID!]
removeSubmissionIDs: [ID!]
@@ -1242,6 +1372,22 @@ input UpdateUserInput {
type User implements Node {
avatar: String
+ cheatRecords(
+ """Returns the elements in the list that come after the specified cursor."""
+ after: Cursor
+ """
+ Returns the elements in the list that come before the specified cursor.
+ """
+ before: Cursor
+ """Returns the first _n_ elements from the list."""
+ first: Int
+ """Returns the last _n_ elements from the list."""
+ last: Int
+ """Filtering options for CheatRecords returned from the connection."""
+ where: CheatRecordWhereInput
+ ): CheatRecordConnection!
+ """Does this user have any existing unresolved cheat records?"""
+ cheating: Boolean!
createdAt: Time!
deletedAt: Time
email: String!
@@ -1397,6 +1543,9 @@ input UserWhereInput {
emailLTE: String
emailNEQ: String
emailNotIn: [String!]
+ """cheat_records edge predicates"""
+ hasCheatRecords: Boolean
+ hasCheatRecordsWith: [CheatRecordWhereInput!]
"""events edge predicates"""
hasEvents: Boolean
hasEventsWith: [EventWhereInput!]