Apply i18n
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
const priorities = [
|
const priorities = [
|
||||||
{ name: "Critical", uid: "critical", color: "#d00002" },
|
{ uid: "critical", color: "#d00002" },
|
||||||
{ name: "High", uid: "high", color: "#ee6b4e" },
|
{ uid: "high", color: "#ee6b4e" },
|
||||||
{ name: "Medium", uid: "medium", color: "#fccb69" },
|
{ uid: "medium", color: "#fccb69" },
|
||||||
{ name: "Low", uid: "low", color: "#0b62e8" },
|
{ uid: "low", color: "#0b62e8" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const testTypes = [
|
const testTypes = [
|
||||||
@@ -43,11 +43,15 @@ const testRunStatus = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const testRunCaseStatus = [
|
const testRunCaseStatus = [
|
||||||
{ name: "Untested", uid: "untested", color: "primary", chartColor: "#e5e7eb" },
|
{
|
||||||
{ name: "Passed", uid: "passed", color: "success", chartColor: "#059669" },
|
uid: "untested",
|
||||||
{ name: "Failed", uid: "failed", color: "danger", chartColor: "#f87171" },
|
color: "primary",
|
||||||
{ name: "Retest", uid: "retest", color: "warning", chartColor: "#fbbf24" },
|
chartColor: "#e5e7eb",
|
||||||
{ name: "Skipped", uid: "skipped", color: "primary", chartColor: "#4b5563" },
|
},
|
||||||
|
{ uid: "passed", color: "success", chartColor: "#059669" },
|
||||||
|
{ uid: "failed", color: "danger", chartColor: "#f87171" },
|
||||||
|
{ uid: "retest", color: "warning", chartColor: "#fbbf24" },
|
||||||
|
{ uid: "skipped", color: "primary", chartColor: "#4b5563" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -40,11 +40,41 @@
|
|||||||
"Cases": {
|
"Cases": {
|
||||||
"test_cases": "Test Cases",
|
"test_cases": "Test Cases",
|
||||||
"id": "ID",
|
"id": "ID",
|
||||||
"name": "Name",
|
"title": "Title",
|
||||||
"priority": "Priority",
|
"priority": "Priority",
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"delete_case": "Delete test case",
|
"delete_case": "Delete test case",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"new_test_case": "New Test Case"
|
"new_test_case": "New Test Case",
|
||||||
|
"status": "Status",
|
||||||
|
"critical": "Critical",
|
||||||
|
"high": "High",
|
||||||
|
"medium": "Medium",
|
||||||
|
"low": "Low"
|
||||||
|
},
|
||||||
|
"Case": {
|
||||||
|
"critical": "Critical",
|
||||||
|
"high": "High",
|
||||||
|
"medium": "Medium",
|
||||||
|
"low": "Low"
|
||||||
|
},
|
||||||
|
"Runs": {
|
||||||
|
"id": "ID",
|
||||||
|
"title": "Title",
|
||||||
|
"priority": "Priority",
|
||||||
|
"status": "Status",
|
||||||
|
"actions": "Actions",
|
||||||
|
"critical": "Critical",
|
||||||
|
"high": "High",
|
||||||
|
"medium": "Medium",
|
||||||
|
"low": "Low",
|
||||||
|
"untested": "Untested",
|
||||||
|
"passed": "Passed",
|
||||||
|
"failed": "Failed",
|
||||||
|
"retest": "Retest",
|
||||||
|
"skipped": "Skipped",
|
||||||
|
"include_in_run": "Include in run",
|
||||||
|
"exclude_from_run": "Exclude from run",
|
||||||
|
"no_cases_found": "No cases found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,36 @@
|
|||||||
"actions": "アクション",
|
"actions": "アクション",
|
||||||
"delete_case": "テストケースを削除",
|
"delete_case": "テストケースを削除",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
"new_test_case": "新規テストケース"
|
"new_test_case": "新規テストケース",
|
||||||
|
"status": "ステータス",
|
||||||
|
"critical": "致",
|
||||||
|
"high": "高",
|
||||||
|
"medium": "中",
|
||||||
|
"low": "低"
|
||||||
|
},
|
||||||
|
"Case": {
|
||||||
|
"critical": "致",
|
||||||
|
"high": "高",
|
||||||
|
"medium": "中",
|
||||||
|
"low": "低"
|
||||||
|
},
|
||||||
|
"Runs": {
|
||||||
|
"id": "ID",
|
||||||
|
"title": "タイトル",
|
||||||
|
"priority": "優先度",
|
||||||
|
"status": "ステータス",
|
||||||
|
"actions": "アクション",
|
||||||
|
"critical": "致",
|
||||||
|
"high": "高",
|
||||||
|
"medium": "中",
|
||||||
|
"low": "低",
|
||||||
|
"untested": "未実行",
|
||||||
|
"passed": "成功",
|
||||||
|
"failed": "失敗",
|
||||||
|
"retest": "再テスト",
|
||||||
|
"skipped": "スキップ",
|
||||||
|
"include_in_run": "テストランに含める",
|
||||||
|
"exclude_from_run": "テストランから除外する",
|
||||||
|
"no_cases_found": "テストケースが見つかりません"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default function TestCaseTable({
|
|||||||
color={priorities[cellValue].color}
|
color={priorities[cellValue].color}
|
||||||
fill={priorities[cellValue].color}
|
fill={priorities[cellValue].color}
|
||||||
/>
|
/>
|
||||||
<div className="ms-3">{priorities[cellValue].name}</div>
|
<div className="ms-3">{messages[priorities[cellValue].uid]}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "actions":
|
case "actions":
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Save, Plus, ArrowLeft, ArrowUpFromLine, Circle } from "lucide-react";
|
|||||||
import { priorities, testTypes, templates } from "@/config/selection";
|
import { priorities, testTypes, templates } from "@/config/selection";
|
||||||
import CaseStepsEditor from "./CaseStepsEditor";
|
import CaseStepsEditor from "./CaseStepsEditor";
|
||||||
import CaseAttachmentsEditor from "./CaseAttachmentsEditor";
|
import CaseAttachmentsEditor from "./CaseAttachmentsEditor";
|
||||||
import { CaseType, AttachmentType } from "@/types/case";
|
import { CaseType, AttachmentType, CaseMessages } from "@/types/case";
|
||||||
import { fetchCase, updateCase } from "../caseControl";
|
import { fetchCase, updateCase } from "../caseControl";
|
||||||
import { fetchCreateStep, fetchDeleteStep } from "./stepControl";
|
import { fetchCreateStep, fetchDeleteStep } from "./stepControl";
|
||||||
import {
|
import {
|
||||||
@@ -44,6 +44,7 @@ export default function CaseEditor({
|
|||||||
projectId: string;
|
projectId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
caseId: string;
|
caseId: string;
|
||||||
|
messages: CaseMessages;
|
||||||
locale: string;
|
locale: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
@@ -253,7 +254,7 @@ export default function CaseEditor({
|
|||||||
>
|
>
|
||||||
{priorities.map((priority, index) => (
|
{priorities.map((priority, index) => (
|
||||||
<SelectItem key={priority.uid} value={index}>
|
<SelectItem key={priority.uid} value={index}>
|
||||||
{priority.name}
|
{params.messages[priority.uid]}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CaseEditor from "./CaseEditor";
|
import CaseEditor from "./CaseEditor";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params,
|
params,
|
||||||
@@ -10,12 +11,21 @@ export default function Page({
|
|||||||
locale: string;
|
locale: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
|
const t = useTranslations("Case");
|
||||||
|
const messages = {
|
||||||
|
critical: t("critical"),
|
||||||
|
high: t("high"),
|
||||||
|
medium: t("medium"),
|
||||||
|
low: t("low"),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CaseEditor
|
<CaseEditor
|
||||||
params={{
|
params={{
|
||||||
projectId: params.projectId,
|
projectId: params.projectId,
|
||||||
folderId: params.folderId,
|
folderId: params.folderId,
|
||||||
caseId: params.caseId,
|
caseId: params.caseId,
|
||||||
|
messages: messages,
|
||||||
locale: params.locale,
|
locale: params.locale,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ export default function Page({
|
|||||||
const t = useTranslations("Cases");
|
const t = useTranslations("Cases");
|
||||||
const messages = {
|
const messages = {
|
||||||
testCases: t("test_cases"),
|
testCases: t("test_cases"),
|
||||||
// folder: t("folder"),
|
|
||||||
// newFolder: t("new_folder"),
|
|
||||||
|
|
||||||
// deleteFolder: t("delete_folder"),
|
|
||||||
id: t("id"),
|
id: t("id"),
|
||||||
title: t("title"),
|
title: t("title"),
|
||||||
priority: t("priority"),
|
priority: t("priority"),
|
||||||
@@ -20,12 +16,11 @@ export default function Page({
|
|||||||
deleteCase: t("delete_case"),
|
deleteCase: t("delete_case"),
|
||||||
delete: t("delete"),
|
delete: t("delete"),
|
||||||
newTestCase: t("new_test_case"),
|
newTestCase: t("new_test_case"),
|
||||||
// projectName: t("folder_name"),
|
status: t("status"),
|
||||||
// projectDetail: t("folder_detail"),
|
critical: t("critical"),
|
||||||
// close: t("close"),
|
high: t("high"),
|
||||||
// create: t("create"),
|
medium: t("medium"),
|
||||||
// update: t("update"),
|
low: t("low"),
|
||||||
// pleaseEnter: t("please_enter"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
RunCaseType,
|
RunCaseType,
|
||||||
RunCaseInfoType,
|
RunCaseInfoType,
|
||||||
RunStatusCountType,
|
RunStatusCountType,
|
||||||
|
RunsMessages,
|
||||||
} from "@/types/run";
|
} from "@/types/run";
|
||||||
import { CaseType } from "@/types/case";
|
import { CaseType } from "@/types/case";
|
||||||
import { FolderType } from "@/types/folder";
|
import { FolderType } from "@/types/folder";
|
||||||
@@ -63,10 +64,11 @@ const defaultTestRun = {
|
|||||||
type Props = {
|
type Props = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
runId: string;
|
runId: string;
|
||||||
|
messages: RunsMessages;
|
||||||
locale: string;
|
locale: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RunEditor({ projectId, runId, locale }: Props) {
|
export default function RunEditor({ projectId, runId, messages, locale }: Props) {
|
||||||
const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
|
const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
|
||||||
const [folders, setFolders] = useState([]);
|
const [folders, setFolders] = useState([]);
|
||||||
const [runCases, setRunCases] = useState<RunCaseType[]>([]);
|
const [runCases, setRunCases] = useState<RunCaseType[]>([]);
|
||||||
@@ -338,13 +340,13 @@ export default function RunEditor({ projectId, runId, locale }: Props) {
|
|||||||
startContent={<CopyPlus size={16} />}
|
startContent={<CopyPlus size={16} />}
|
||||||
onClick={() => handleBulkIncludeExcludeCases(true)}
|
onClick={() => handleBulkIncludeExcludeCases(true)}
|
||||||
>
|
>
|
||||||
Include selected cases in run
|
{messages.includeInRun}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
startContent={<CopyMinus size={16} />}
|
startContent={<CopyMinus size={16} />}
|
||||||
onClick={() => handleBulkIncludeExcludeCases(false)}
|
onClick={() => handleBulkIncludeExcludeCases(false)}
|
||||||
>
|
>
|
||||||
Exclude selected cases from run
|
{messages.excludeFromRun}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@@ -385,6 +387,7 @@ export default function RunEditor({ projectId, runId, locale }: Props) {
|
|||||||
onExcludeCase={(excludeCaseId) =>
|
onExcludeCase={(excludeCaseId) =>
|
||||||
handleIncludeExcludeCase(false, excludeCaseId)
|
handleIncludeExcludeCase(false, excludeCaseId)
|
||||||
}
|
}
|
||||||
|
messages={messages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,14 +27,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { priorities, testRunCaseStatus } from "@/config/selection";
|
import { priorities, testRunCaseStatus } from "@/config/selection";
|
||||||
import { CaseType } from "@/types/case";
|
import { CaseType } from "@/types/case";
|
||||||
|
import { RunsMessages } from "@/types/run";
|
||||||
const headerColumns = [
|
|
||||||
{ name: "ID", uid: "id", sortable: true },
|
|
||||||
{ name: "Title", uid: "title", sortable: true },
|
|
||||||
{ name: "Priority", uid: "priority", sortable: true },
|
|
||||||
{ name: "Status", uid: "runStatus", sortable: true },
|
|
||||||
{ name: "Actions", uid: "actions" },
|
|
||||||
];
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cases: CaseType[];
|
cases: CaseType[];
|
||||||
@@ -43,6 +36,7 @@ type Props = {
|
|||||||
onStatusChange: (changeCaseId: number, status: number) => {};
|
onStatusChange: (changeCaseId: number, status: number) => {};
|
||||||
onIncludeCase: (includeCaseId: number) => {};
|
onIncludeCase: (includeCaseId: number) => {};
|
||||||
onExcludeCase: (excludeCaseId: number) => {};
|
onExcludeCase: (excludeCaseId: number) => {};
|
||||||
|
messages: RunsMessages;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function TestCaseSelector({
|
export default function TestCaseSelector({
|
||||||
@@ -52,7 +46,16 @@ export default function TestCaseSelector({
|
|||||||
onStatusChange,
|
onStatusChange,
|
||||||
onIncludeCase,
|
onIncludeCase,
|
||||||
onExcludeCase,
|
onExcludeCase,
|
||||||
|
messages,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const headerColumns = [
|
||||||
|
{ name: messages.id, uid: "id", sortable: true },
|
||||||
|
{ name: messages.title, uid: "title", sortable: true },
|
||||||
|
{ name: messages.priority, uid: "priority", sortable: true },
|
||||||
|
{ name: messages.status, uid: "runStatus", sortable: true },
|
||||||
|
{ name: messages.actions, uid: "actions" },
|
||||||
|
];
|
||||||
|
|
||||||
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
|
||||||
column: "id",
|
column: "id",
|
||||||
direction: "ascending",
|
direction: "ascending",
|
||||||
@@ -102,7 +105,7 @@ export default function TestCaseSelector({
|
|||||||
color={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
color={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
||||||
fill={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
fill={isIncluded ? priorities[cellValue].color : "#d4d4d8"}
|
||||||
/>
|
/>
|
||||||
<div className="ms-3">{priorities[cellValue].name}</div>
|
<div className="ms-3">{messages[priorities[cellValue].uid]}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "runStatus":
|
case "runStatus":
|
||||||
@@ -120,7 +123,7 @@ export default function TestCaseSelector({
|
|||||||
endContent={isIncluded && <ChevronDown size={16} />}
|
endContent={isIncluded && <ChevronDown size={16} />}
|
||||||
>
|
>
|
||||||
<span className="w-12">
|
<span className="w-12">
|
||||||
{isIncluded && testRunCaseStatus[cellValue].name}
|
{isIncluded && messages[testRunCaseStatus[cellValue].uid]}
|
||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownTrigger>
|
</DropdownTrigger>
|
||||||
@@ -131,7 +134,7 @@ export default function TestCaseSelector({
|
|||||||
startContent={renderStatusIcon(runCaseStatus.uid)}
|
startContent={renderStatusIcon(runCaseStatus.uid)}
|
||||||
onPress={() => onStatusChange(testCase.id, index)}
|
onPress={() => onStatusChange(testCase.id, index)}
|
||||||
>
|
>
|
||||||
{runCaseStatus.name}
|
{messages[runCaseStatus.uid]}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -151,14 +154,14 @@ export default function TestCaseSelector({
|
|||||||
isDisabled={testCase.isIncluded}
|
isDisabled={testCase.isIncluded}
|
||||||
onPress={() => onIncludeCase(testCase.id)}
|
onPress={() => onIncludeCase(testCase.id)}
|
||||||
>
|
>
|
||||||
Include in run
|
{messages.includeInRun}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
startContent={<CopyMinus size={16} />}
|
startContent={<CopyMinus size={16} />}
|
||||||
isDisabled={!testCase.isIncluded}
|
isDisabled={!testCase.isIncluded}
|
||||||
onPress={() => onExcludeCase(testCase.id)}
|
onPress={() => onExcludeCase(testCase.id)}
|
||||||
>
|
>
|
||||||
Exclude from run
|
{messages.excludeFromRun}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
@@ -215,7 +218,7 @@ export default function TestCaseSelector({
|
|||||||
</TableColumn>
|
</TableColumn>
|
||||||
)}
|
)}
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody emptyContent={"No cases found"} items={sortedItems}>
|
<TableBody emptyContent={messages.noCasesFound} items={sortedItems}>
|
||||||
{(item) => (
|
{(item) => (
|
||||||
<TableRow
|
<TableRow
|
||||||
key={item.id}
|
key={item.id}
|
||||||
|
|||||||
@@ -1,14 +1,37 @@
|
|||||||
import RunEditor from "./RunEditor";
|
import RunEditor from "./RunEditor";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
export default function Page({
|
export default function Page({
|
||||||
params,
|
params,
|
||||||
}: {
|
}: {
|
||||||
params: { projectId: string; runId: string; locale: string };
|
params: { projectId: string; runId: string; locale: string };
|
||||||
}) {
|
}) {
|
||||||
|
const t = useTranslations("Runs");
|
||||||
|
const messages = {
|
||||||
|
id: t("id"),
|
||||||
|
title: t("title"),
|
||||||
|
priority: t("priority"),
|
||||||
|
actions: t("actions"),
|
||||||
|
status: t("status"),
|
||||||
|
critical: t("critical"),
|
||||||
|
high: t("high"),
|
||||||
|
medium: t("medium"),
|
||||||
|
low: t("low"),
|
||||||
|
untested: t("untested"),
|
||||||
|
passed: t("passed"),
|
||||||
|
failed: t("failed"),
|
||||||
|
retest: t("retest"),
|
||||||
|
skipped: t("skipped"),
|
||||||
|
includeInRun: t("include_in_run"),
|
||||||
|
excludeFromRun: t("exclude_from_run"),
|
||||||
|
noCasesFound: t("no_cases_found"),
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RunEditor
|
<RunEditor
|
||||||
projectId={params.projectId}
|
projectId={params.projectId}
|
||||||
runId={params.runId}
|
runId={params.runId}
|
||||||
|
messages={messages}
|
||||||
locale={params.locale}
|
locale={params.locale}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,6 +58,18 @@ export type CasesMessages = {
|
|||||||
deleteCase: string;
|
deleteCase: string;
|
||||||
delete: string;
|
delete: string;
|
||||||
newTestCase: string;
|
newTestCase: string;
|
||||||
|
status: string;
|
||||||
|
critical: string;
|
||||||
|
high: string;
|
||||||
|
medium: string;
|
||||||
|
low: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { CaseType, StepType, AttachmentType, CasesMessages };
|
export type CaseMessages = {
|
||||||
|
critical: string;
|
||||||
|
high: string;
|
||||||
|
medium: string;
|
||||||
|
low: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { CaseType, StepType, AttachmentType, CasesMessages, CaseMessages };
|
||||||
|
|||||||
@@ -26,4 +26,30 @@ type RunStatusCountType = {
|
|||||||
count: number;
|
count: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { RunType, RunCaseType, RunCaseInfoType, RunStatusCountType };
|
export type RunsMessages = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
priority: string;
|
||||||
|
status: string;
|
||||||
|
actions: string;
|
||||||
|
critical: string;
|
||||||
|
high: string;
|
||||||
|
medium: string;
|
||||||
|
low: string;
|
||||||
|
untested: string;
|
||||||
|
passed: string;
|
||||||
|
failed: string;
|
||||||
|
retest: string;
|
||||||
|
skipped: string;
|
||||||
|
includeInRun: string;
|
||||||
|
excludeFromRun: string;
|
||||||
|
noCasesFound: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
RunType,
|
||||||
|
RunCaseType,
|
||||||
|
RunCaseInfoType,
|
||||||
|
RunStatusCountType,
|
||||||
|
RunsMessages,
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user