More styling
This commit is contained in:
@@ -2,9 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/gun.jpg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>Pull the Trigger</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "vite build",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
public/gun.jpg
Normal file
BIN
public/gun.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
24
src/App.tsx
24
src/App.tsx
@@ -20,6 +20,8 @@ import ThemeProvider from "./components/ThemeProvider";
|
|||||||
import "@mantine/core/styles.css"; //import Mantine V7 styles needed by MRT
|
import "@mantine/core/styles.css"; //import Mantine V7 styles needed by MRT
|
||||||
import "@mantine/dates/styles.css"; //if using mantine date picker features
|
import "@mantine/dates/styles.css"; //if using mantine date picker features
|
||||||
import "mantine-react-table/styles.css"; //import MRT styles
|
import "mantine-react-table/styles.css"; //import MRT styles
|
||||||
|
import { appTheme } from "./contexts/theme";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { data, isFetched } = useQueryAPI({
|
const { data, isFetched } = useQueryAPI({
|
||||||
@@ -31,29 +33,29 @@ function App() {
|
|||||||
defaultValue: "blackboard",
|
defaultValue: "blackboard",
|
||||||
});
|
});
|
||||||
|
|
||||||
const [currentTheme, setCurrentTheme] = useState<any>(undefined);
|
const [theme, setTheme] = useAtom(appTheme);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data && themeName) {
|
if (data && themeName) {
|
||||||
const temp = data.find((t: any) => t.name === themeName);
|
const temp = data.find((t: any) => t.name === themeName);
|
||||||
setCurrentTheme(temp);
|
setTheme(temp);
|
||||||
}
|
}
|
||||||
}, [isFetched, themeName]);
|
}, [isFetched, themeName]);
|
||||||
|
|
||||||
const router = createBrowserRouter(
|
const router = createBrowserRouter(
|
||||||
createRoutesFromElements(
|
createRoutesFromElements(
|
||||||
<Route>
|
<Route>
|
||||||
<Route path="/trigger" element={<Homepage theme={currentTheme} />} />
|
<Route path="/trigger" element={<Homepage theme={theme} />} />
|
||||||
<Route
|
<Route
|
||||||
path="/trigger/settings"
|
path="/trigger/settings"
|
||||||
element={<Settings theme={currentTheme} />}
|
element={<Settings theme={theme} />}
|
||||||
/>
|
/>
|
||||||
</Route>,
|
</Route>,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const overrides = createTheme({
|
const overrides = createTheme({
|
||||||
white: currentTheme?.colorbackground,
|
white: theme?.colorbackground,
|
||||||
fontFamily:
|
fontFamily:
|
||||||
"-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif",
|
"-apple-system,Jost,BlinkMacSystemFont,Helvetica Neue,Roboto,sans-serif",
|
||||||
});
|
});
|
||||||
@@ -61,15 +63,19 @@ function App() {
|
|||||||
const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides);
|
const mantineTheme = mergeMantineTheme(DEFAULT_THEME, overrides);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{theme && (
|
||||||
<MantineProvider theme={mantineTheme}>
|
<MantineProvider theme={mantineTheme}>
|
||||||
{currentTheme && (
|
<ThemeProvider theme={theme}>
|
||||||
<ThemeProvider theme={currentTheme}>
|
<ModalsProvider >
|
||||||
<ModalsProvider>
|
|
||||||
<RouterProvider router={router} />
|
<RouterProvider router={router} />
|
||||||
</ModalsProvider>
|
</ModalsProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)}
|
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import {
|
|||||||
} from "mantine-react-table";
|
} from "mantine-react-table";
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import "../dataTableStyles.css";
|
import "../dataTableStyles.css";
|
||||||
import { ActionIcon, Button, Flex, Text, Tooltip } from "@mantine/core";
|
import { ActionIcon, Button, Flex, Text } from "@mantine/core";
|
||||||
import { FiPlus } from "react-icons/fi";
|
import { FiPlus } from "react-icons/fi";
|
||||||
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
import { IconEdit, IconTrash } from "@tabler/icons-react";
|
||||||
import { useMutationAPI } from "../hooks/useAPI";
|
import { useMutationAPI } from "../hooks/useAPI";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
|
import { actionIconClasses, buttonClasses } from "../styles";
|
||||||
|
import { appTheme } from "../contexts/theme";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
|
||||||
export function DataTableComponent({
|
export function DataTableComponent({
|
||||||
tableName,
|
tableName,
|
||||||
@@ -27,10 +30,10 @@ export function DataTableComponent({
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
endpoint: string;
|
endpoint: string;
|
||||||
}) {
|
}) {
|
||||||
const { mutateAsync: createRow, isPending: isCreatingRow } = useMutationAPI();
|
const [theme, setTheme] = useAtom(appTheme);
|
||||||
const { mutateAsync: editRow, isPending: isEditingRow } = useMutationAPI();
|
const { mutateAsync: createRow } = useMutationAPI();
|
||||||
const { mutateAsync: deleteRow, isPending: isDeletingRow } = useMutationAPI();
|
const { mutateAsync: editRow } = useMutationAPI();
|
||||||
|
const { mutateAsync: deleteRow } = useMutationAPI();
|
||||||
const handleCreateRow: MRT_TableOptions<any>["onCreatingRowSave"] = async ({
|
const handleCreateRow: MRT_TableOptions<any>["onCreatingRowSave"] = async ({
|
||||||
values,
|
values,
|
||||||
exitCreatingMode,
|
exitCreatingMode,
|
||||||
@@ -45,7 +48,7 @@ export function DataTableComponent({
|
|||||||
editRow({ method: "PATCH", endpoint: endpoint, body: values });
|
editRow({ method: "PATCH", endpoint: endpoint, body: values });
|
||||||
table.setEditingRow(null);
|
table.setEditingRow(null);
|
||||||
};
|
};
|
||||||
|
console.log(theme)
|
||||||
const openDeleteConfirmModal = (row: MRT_Row<any>) =>
|
const openDeleteConfirmModal = (row: MRT_Row<any>) =>
|
||||||
modals.openConfirmModal({
|
modals.openConfirmModal({
|
||||||
title: "Are you sure you want to delete this user?",
|
title: "Are you sure you want to delete this user?",
|
||||||
@@ -55,7 +58,6 @@ export function DataTableComponent({
|
|||||||
onConfirm: () =>
|
onConfirm: () =>
|
||||||
deleteRow({ method: "DELETE", endpoint: endpoint, body: row.original }),
|
deleteRow({ method: "DELETE", endpoint: endpoint, body: row.original }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const table = useMantineReactTable({
|
const table = useMantineReactTable({
|
||||||
columns: columns,
|
columns: columns,
|
||||||
data: tableData,
|
data: tableData,
|
||||||
@@ -80,36 +82,25 @@ export function DataTableComponent({
|
|||||||
showLoadingOverlay: isLoading,
|
showLoadingOverlay: isLoading,
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
},
|
},
|
||||||
mantineTableHeadProps: {
|
mantinePaperProps: {className: 'root'},
|
||||||
style: {
|
|
||||||
color: "var(--color-text-pri)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mantineTableBodyCellProps: {
|
|
||||||
style: {
|
|
||||||
color: "var(--color-text-pri)",
|
|
||||||
background: "var(--color-background)",
|
|
||||||
borderRight: "none",
|
|
||||||
borderLeft: "none",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
renderRowActions: ({ row, table }) => (
|
renderRowActions: ({ row, table }) => (
|
||||||
<Flex gap="md">
|
<Flex gap="md">
|
||||||
<Tooltip label="Edit">
|
<ActionIcon className={actionIconClasses.editButton} classNames={actionIconClasses} onClick={() => table.setEditingRow(row)}>
|
||||||
<ActionIcon onClick={() => table.setEditingRow(row)}>
|
|
||||||
<IconEdit />
|
<IconEdit />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
|
||||||
<Tooltip label="Delete">
|
|
||||||
<ActionIcon color="red" onClick={() => openDeleteConfirmModal(row)}>
|
<ActionIcon color="red" onClick={() => openDeleteConfirmModal(row)}>
|
||||||
<IconTrash />
|
<IconTrash />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
|
mantineCreateRowModalProps: {
|
||||||
|
style: {
|
||||||
|
color: theme.colortextpri,
|
||||||
|
},
|
||||||
|
},
|
||||||
renderTopToolbarCustomActions: ({ table }) => {
|
renderTopToolbarCustomActions: ({ table }) => {
|
||||||
return (
|
return (
|
||||||
<Button onClick={() => table.setCreatingRow(true)}>
|
<Button className={buttonClasses.tableButton} classNames={buttonClasses} disabled={tableName === "Personalization"} onClick={() => table.setCreatingRow(true)}>
|
||||||
<FiPlus />
|
<FiPlus />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
5
src/contexts/theme.ts
Normal file
5
src/contexts/theme.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
const appTheme = atom<any>({});
|
||||||
|
|
||||||
|
export { appTheme }
|
||||||
@@ -1,31 +1,31 @@
|
|||||||
.mrt-table-head-sort-button {
|
.root {
|
||||||
display: none;
|
--mrt-base-background-color: var(--color-background);
|
||||||
|
--mrt-striped-row-background-color: var();
|
||||||
|
--mrt-row-hover-background-color: var();
|
||||||
|
--mrt-striped-row-hover-background-color: var();
|
||||||
|
--mrt-selected-row-background-color: var();
|
||||||
|
--mrt-selected-row-hover-background-color: var();
|
||||||
|
--mrt-pinned-row-background-color: var();
|
||||||
|
--mrt-pinned-row-hover-background-color: var();
|
||||||
|
--mrt-pinned-column-background-color: var();
|
||||||
|
--mrt-dragging-hovered-border-color: var();
|
||||||
|
--mrt-dragging-drag-border-color: var();
|
||||||
|
--mrt-resize-column-border-color: var();
|
||||||
color: var(--color-text-pri) !important;
|
color: var(--color-text-pri) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mrt-table-head-sort-button[data-sorted='asc'] {
|
.mrt-table-head-sort-button {
|
||||||
display: flex;
|
color: var(--color-text-pri) !important;
|
||||||
min-height: 12px !important;
|
|
||||||
min-width: 12px !important;
|
|
||||||
max-width: 16px;
|
|
||||||
max-height: 16px;
|
|
||||||
|
|
||||||
& > * > .tabler-icon-arrows-sort {
|
|
||||||
height: 12px !important;
|
|
||||||
width: 12px !important;
|
|
||||||
color: light-dark(--mantine-primary-color-6, --mantine-color-appGray-5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.mrt-table-head-sort-button[data-sorted='desc'] {
|
|
||||||
display: flex;
|
|
||||||
min-height: 12px !important;
|
|
||||||
min-width: 12px !important;
|
|
||||||
max-width: 16px;
|
|
||||||
max-height: 16px;
|
|
||||||
|
|
||||||
& > * > .tabler-icon-arrows-sort {
|
.mantine-Input-input {
|
||||||
height: 12px !important;
|
color: var(--color-text-pri) !important;
|
||||||
width: 12px !important;
|
}
|
||||||
color: light-dark(--mantine-primary-color-6, --mantine-color-appGray-5);
|
|
||||||
}
|
.mantine-Select-input {
|
||||||
|
color: var(--color-text-pri) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mantine-Select-option {
|
||||||
|
color: var(--color-text-pri) !important;
|
||||||
}
|
}
|
||||||
@@ -1 +1,12 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100;0,200;0,400;1,100;1,200&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100;0,200;0,400;1,100;1,200&display=swap');
|
||||||
|
|
||||||
|
.mrt-edit-action-buttons {
|
||||||
|
.mantine-Button-root[data-variant="subtle"] {
|
||||||
|
color: var(--color-text-pri) !important;
|
||||||
|
background-color: var(--color-text-pri) !important;
|
||||||
|
}
|
||||||
|
.mantine-Button-root[data-variant="filled"] {
|
||||||
|
color: var(--color-text-pri) !important;
|
||||||
|
background-color: var(--color-text-pri) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,12 +10,19 @@ export default function Search() {
|
|||||||
const [providerURL, setProviderURL] = useState(
|
const [providerURL, setProviderURL] = useState(
|
||||||
"https://www.google.com/search?q=",
|
"https://www.google.com/search?q=",
|
||||||
);
|
);
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
|
||||||
const handleKeyPress = (item: any) => {
|
const handleKeyPress = (item: any) => {
|
||||||
const option =
|
let option = ''
|
||||||
|
let subtext = ''
|
||||||
|
if (item.target.value.startsWith("/")) {
|
||||||
|
option =
|
||||||
item.target.value.substr(1, item.target.value.indexOf(" ") - 1) ||
|
item.target.value.substr(1, item.target.value.indexOf(" ") - 1) ||
|
||||||
item.target.value.substr(1);
|
item.target.value.substr(1);
|
||||||
const subtext = item.target.value.substr(2 + option.length);
|
subtext = item.target.value.substr(2 + option.length);
|
||||||
|
} else {
|
||||||
|
subtext = item.target.value;
|
||||||
|
}
|
||||||
if (item.key === "Enter") {
|
if (item.key === "Enter") {
|
||||||
if (item.target.value.startsWith("/")) {
|
if (item.target.value.startsWith("/")) {
|
||||||
open(`${providerURL}${subtext}`, "_self");
|
open(`${providerURL}${subtext}`, "_self");
|
||||||
@@ -63,25 +70,25 @@ export default function Search() {
|
|||||||
classNames={textInputClasses}
|
classNames={textInputClasses}
|
||||||
spellCheck="true"
|
spellCheck="true"
|
||||||
leftSection={providerLabel}
|
leftSection={providerLabel}
|
||||||
|
value={value}
|
||||||
|
data-active={value !== ''}
|
||||||
onChange={(item: any) => {
|
onChange={(item: any) => {
|
||||||
|
setValue(item.currentTarget.value)
|
||||||
if (item.target.value.startsWith("/")) {
|
if (item.target.value.startsWith("/")) {
|
||||||
setProviderLabel(
|
const provider = providers?.find(
|
||||||
providers?.find(
|
|
||||||
(p: any) => p.prefix === item.target.value.split(" ")[0],
|
(p: any) => p.prefix === item.target.value.split(" ")[0],
|
||||||
).name,
|
)
|
||||||
);
|
setProviderLabel(provider?.name || "Invalid Search");
|
||||||
setProviderURL(
|
setProviderURL(provider?.url);
|
||||||
providers?.find(
|
|
||||||
(p: any) => p.prefix === item.target.value.split(" ")[0],
|
|
||||||
).url,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
setProviderLabel("Google Search");
|
setProviderLabel("Google Search");
|
||||||
setProviderURL("https://www.google.com/search?q=");
|
setProviderURL("https://www.google.com/search?q=");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onKeyUp={(item: any) => {
|
onKeyUp={(item: any) => {
|
||||||
|
if(providerLabel !== "Invalid Search"){
|
||||||
handleKeyPress(item);
|
handleKeyPress(item);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import {
|
|||||||
ColorPicker,
|
ColorPicker,
|
||||||
ColorSwatch,
|
ColorSwatch,
|
||||||
Divider,
|
Divider,
|
||||||
Grid,
|
|
||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
SimpleGrid,
|
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
@@ -19,7 +17,7 @@ import { FiEdit, FiPlus, FiSave, FiTrash, FiX } from "react-icons/fi";
|
|||||||
import { useDisclosure, useLocalStorage } from "@mantine/hooks";
|
import { useDisclosure, useLocalStorage } from "@mantine/hooks";
|
||||||
import "@mantine/core/styles/ModalBase.css";
|
import "@mantine/core/styles/ModalBase.css";
|
||||||
import "@mantine/core/styles/Modal.css";
|
import "@mantine/core/styles/Modal.css";
|
||||||
import { actionIconClasses, modalClasses, textClasses } from "../../../styles";
|
import { actionIconClasses, buttonClasses, modalClasses, textClasses } from "../../../styles";
|
||||||
import ThemeModalContent from "./ThemeModalContent";
|
import ThemeModalContent from "./ThemeModalContent";
|
||||||
import { modals } from "@mantine/modals";
|
import { modals } from "@mantine/modals";
|
||||||
function ThemesSettings({ theme }: any) {
|
function ThemesSettings({ theme }: any) {
|
||||||
@@ -84,13 +82,6 @@ function ThemesSettings({ theme }: any) {
|
|||||||
<>
|
<>
|
||||||
<Modal.Root
|
<Modal.Root
|
||||||
size={"50%"}
|
size={"50%"}
|
||||||
style={{
|
|
||||||
"--color-text-pri": theme.colortextpri,
|
|
||||||
"--color-text-acc": theme.colortextacc,
|
|
||||||
"--color-background": theme.colorbackground,
|
|
||||||
"--color-border": theme.colorborder,
|
|
||||||
"--color-search-background": theme.colorsearchbackground,
|
|
||||||
}}
|
|
||||||
className={modalClasses.editThemeModal}
|
className={modalClasses.editThemeModal}
|
||||||
classNames={modalClasses}
|
classNames={modalClasses}
|
||||||
opened={opened}
|
opened={opened}
|
||||||
@@ -281,6 +272,8 @@ function ThemesSettings({ theme }: any) {
|
|||||||
Themes
|
Themes
|
||||||
</Text>
|
</Text>
|
||||||
<Button
|
<Button
|
||||||
|
className={buttonClasses.tableButton}
|
||||||
|
classNames={buttonClasses}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedTheme({
|
setSelectedTheme({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
|
|||||||
@@ -33,3 +33,21 @@
|
|||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tableButton {
|
||||||
|
&.root {
|
||||||
|
&.loader{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
background-color: var(--color-text-pri);
|
||||||
|
color: var(--color-text-acc);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-text-acc) !important;
|
||||||
|
color: var(--color-text-pri) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,16 @@
|
|||||||
}
|
}
|
||||||
.tab {
|
.tab {
|
||||||
color: var(--color-text-pri);
|
color: var(--color-text-pri);
|
||||||
|
border-color: var(--color-text-acc);
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--color-background);
|
color: var(--color-background);
|
||||||
background-color: var(--color-text-acc);
|
background-color: var(--color-text-acc);
|
||||||
}
|
}
|
||||||
|
&[data-active=true]{
|
||||||
|
border-color: var(--color-text-pri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.list::before {
|
||||||
|
border-color: var(--color-text-acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,5 +49,20 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.input[data-active="true"]{
|
||||||
|
background-color: var(--color-search-background);
|
||||||
|
border-color: var(--color-border);
|
||||||
|
outline: none;
|
||||||
|
color: var(--color-text-acc);
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.2em;
|
||||||
|
letter-spacing: .2em;
|
||||||
|
width:100%;
|
||||||
|
border-top-left-radius: .25em;
|
||||||
|
border-top-right-radius: .25em;
|
||||||
|
border-bottom-left-radius: .25em;
|
||||||
|
border-bottom-right-radius: .25em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user