304 lines
11 KiB
TypeScript
304 lines
11 KiB
TypeScript
import {
|
||
Box,
|
||
Button,
|
||
FormControl,
|
||
IconButton,
|
||
MenuItem,
|
||
TextField as MuiTextField,
|
||
Paper,
|
||
Select,
|
||
SelectChangeEvent,
|
||
TextFieldProps,
|
||
Typography,
|
||
useMediaQuery,
|
||
useTheme
|
||
} from "@mui/material";
|
||
import Tooltip from "@mui/material/Tooltip";
|
||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||
import { FC, useEffect, useMemo, useState } from "react";
|
||
import ArrowDown from "@/assets/icons/ArrowDownIcon";
|
||
import CopyIcon from "@/assets/icons/CopyIcon";
|
||
import LinkIcon from "@/assets/icons/LinkIcon";
|
||
import { InfoPopover } from "@/ui_kit/InfoPopover";
|
||
import { utmApi, type UtmRecord } from "@/api/utm";
|
||
import { UtmList } from "./UtmList";
|
||
const TextField = MuiTextField as unknown as FC<TextFieldProps>;
|
||
|
||
|
||
export default function QuizMarkCreate() {
|
||
const theme = useTheme();
|
||
const quiz = useCurrentQuiz();
|
||
const [category, setCategory] = useState<string>("google");
|
||
const [name, setName] = useState<string>("");
|
||
const [utm, setUtm] = useState<string>("");
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||
const [items, setItems] = useState<UtmRecord[]>([]);
|
||
const [loading, setLoading] = useState<boolean>(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
let cancelled = false;
|
||
async function load() {
|
||
if (!quiz?.backendId) return;
|
||
setLoading(true);
|
||
setError(null);
|
||
const [data, err] = await utmApi.getAll(quiz.backendId);
|
||
if (cancelled) return;
|
||
if (err) {
|
||
setError(err);
|
||
setItems([]);
|
||
} else {
|
||
setItems((data ?? []).filter((i) => !i.deleted));
|
||
}
|
||
setLoading(false);
|
||
}
|
||
load();
|
||
return () => { cancelled = true; };
|
||
}, [quiz?.backendId]);
|
||
|
||
const CopyLink = () => {
|
||
let one = (document.getElementById("inputMarkLinkone") as HTMLInputElement)
|
||
?.value;
|
||
let text = (document.getElementById("inputMarkLink") as HTMLInputElement)
|
||
?.value;
|
||
navigator.clipboard.writeText(one + text);
|
||
};
|
||
|
||
const handleChange = (event: SelectChangeEvent<string>) => {
|
||
setCategory(event.target.value as string);
|
||
};
|
||
|
||
const filteredItems = useMemo(() => {
|
||
return items.filter((i) => (i.category ? i.category === category : true));
|
||
}, [items, category]);
|
||
|
||
const utmHasSpaces = /\s/.test(utm);
|
||
const canSave = Boolean(name.trim() && category && utm.trim() && !utmHasSpaces && quiz?.backendId);
|
||
|
||
const handleSave = async () => {
|
||
if (!canSave || !quiz?.backendId) return;
|
||
const body = { quiz_id: quiz.backendId, utm, name, category };
|
||
const [created, err] = await utmApi.create(body);
|
||
if (err || !created) return;
|
||
setItems((prev) => [created, ...prev]);
|
||
// Очистим только utm, оставим категорию; имя можно тоже очистить
|
||
setUtm("");
|
||
setName("");
|
||
};
|
||
|
||
if (!quiz) return null;
|
||
|
||
return (
|
||
<Paper
|
||
sx={{
|
||
boxSizing: "border-box",
|
||
maxWidth: "580px",
|
||
width: "100%",
|
||
padding: "22px 18px 20px 20px",
|
||
borderRadius: "12px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
boxShadow:
|
||
"0px 100px 309px rgba(210, 208, 225, 0.24), 0px 41.7776px 129.093px rgba(210, 208, 225, 0.172525), 0px 22.3363px 69.0192px rgba(210, 208, 225, 0.143066), 0px 12.5216px 38.6916px rgba(210, 208, 225, 0.12), 0px 6.6501px 20.5488px rgba(210, 208, 225, 0.0969343), 0px 2.76726px 8.55082px rgba(210, 208, 225, 0.0674749)",
|
||
}}
|
||
>
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
alignItems: isMobile ? "flex-start" : "center",
|
||
gap: "10px",
|
||
flexDirection: isMobile ? "column" : "row",
|
||
}}
|
||
>
|
||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||
<LinkIcon
|
||
color={theme.palette.brightPurple.main}
|
||
bgcolor={"#EEE4FC"}
|
||
/>
|
||
<Typography color={theme.palette.grey3.main}>Создание/выбор utm меток</Typography>
|
||
</Box>
|
||
<Box>
|
||
|
||
<InfoPopover />
|
||
<FormControl
|
||
fullWidth
|
||
size="small"
|
||
sx={{
|
||
|
||
width: "100%",
|
||
maxWidth: "118px",
|
||
height: "24px",
|
||
}}
|
||
>
|
||
<Select
|
||
id="display-select"
|
||
variant="outlined"
|
||
value={category}
|
||
displayEmpty
|
||
onChange={handleChange}
|
||
sx={{
|
||
height: "24px",
|
||
borderRadius: "8px",
|
||
"& .MuiOutlinedInput-notchedOutline": {
|
||
border: "none !important",
|
||
},
|
||
"& .MuiSelect-icon": {
|
||
position: 'relative',
|
||
marginBottom: '13px',
|
||
}
|
||
}}
|
||
MenuProps={{
|
||
PaperProps: {
|
||
sx: {
|
||
mt: "8px",
|
||
p: "4px",
|
||
borderRadius: "8px",
|
||
width: "300px",
|
||
maxWidth: "300px",
|
||
},
|
||
},
|
||
MenuListProps: {
|
||
sx: {
|
||
py: 0,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: "8px",
|
||
"& .Mui-selected": {
|
||
backgroundColor: theme.palette.background.default,
|
||
color: theme.palette.brightPurple.main,
|
||
},
|
||
"& .MuiMenuItem-root": {
|
||
whiteSpace: "normal",
|
||
wordBreak: "break-word",
|
||
lineHeight: "20px",
|
||
maxWidth: "300px",
|
||
},
|
||
},
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
color: theme.palette.brightPurple.main,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
px: "20px",
|
||
gap: "20px"
|
||
},
|
||
}}
|
||
IconComponent={(props) => <ArrowDown {...props} sx={{
|
||
position: 'relative',
|
||
top: '-5px', // Поднимаем вверх
|
||
right: '5px'
|
||
}} />}
|
||
|
||
>
|
||
<MenuItem
|
||
value={"google"}
|
||
sx={{
|
||
display: "flex",
|
||
gap: "20px",
|
||
p: "4px",
|
||
borderRadius: "5px",
|
||
color: theme.palette.grey2.main,
|
||
}}
|
||
>
|
||
Google
|
||
</MenuItem>
|
||
<MenuItem value={"vk"} sx={{ display: "flex", gap: "20px", p: "4px", borderRadius: "5px", color: theme.palette.grey2.main }}>VK</MenuItem>
|
||
<MenuItem value={"facebook"} sx={{ display: "flex", gap: "20px", p: "4px", borderRadius: "5px", color: theme.palette.grey2.main }}>Facebook* является экстремисткой организацией запрещённой на территории РФ</MenuItem>
|
||
<MenuItem value={"yandex"} sx={{ display: "flex", gap: "20px", p: "4px", borderRadius: "5px", color: theme.palette.grey2.main }}>Яндекс</MenuItem>
|
||
<MenuItem value={"tiktok"} sx={{ display: "flex", gap: "20px", p: "4px", borderRadius: "5px", color: theme.palette.grey2.main }}>TikTok</MenuItem>
|
||
<MenuItem value={"other"} sx={{ display: "flex", gap: "20px", p: "4px", borderRadius: "5px", color: theme.palette.grey2.main }}>другое</MenuItem>
|
||
</Select>
|
||
</FormControl>
|
||
</Box>
|
||
</Box>
|
||
<Box sx={{ display: "inline-flex", alignItems: "center", justifyContent: "space-between", margin: "30px 0px 0px 0px" }}>
|
||
<Box sx={{ display: "inline-flex" }}>
|
||
<FormControl variant="standard" sx={{ p: 0 }}>
|
||
<TextField
|
||
id="inputMarkLinkone"
|
||
placeholder="Название"
|
||
value={name}
|
||
onChange={(e) => setName(e.target.value)}
|
||
sx={{
|
||
width: isTablet ? "100%" : "182px",
|
||
maxWidth: "182px",
|
||
"& .MuiInputBase-root": {
|
||
|
||
height: "48px",
|
||
color: "#525253",
|
||
borderRadius: "8px 0 0 8px",
|
||
backgroundColor: "#F2F3F7",
|
||
},
|
||
}}
|
||
/>
|
||
</FormControl>
|
||
<FormControl variant="standard" sx={{ p: 0 }}>
|
||
<TextField
|
||
placeholder="пример-ссылки-с-utm-метками"
|
||
id="inputMarkLink"
|
||
value={utm}
|
||
onChange={(e) => setUtm(e.target.value)}
|
||
|
||
sx={{
|
||
|
||
"& .MuiInputBase-root": {
|
||
color: "#525253",
|
||
width: isTablet ? "100%" : "317px",
|
||
maxWidth: "317px",
|
||
height: "48px",
|
||
borderRadius: "0 8px 8px 0",
|
||
backgroundColor: "#F2F3F7"
|
||
},
|
||
}}
|
||
inputProps={{
|
||
sx: {
|
||
borderRadius: "0 10px 10px 0",
|
||
fontSize: "18px",
|
||
lineHeight: "21px",
|
||
py: 0,
|
||
},
|
||
}}
|
||
/>
|
||
</FormControl>
|
||
|
||
<IconButton
|
||
onClick={CopyLink}
|
||
id={"copyLink"}
|
||
sx={{ borderRadius: "6px" }}
|
||
>
|
||
<CopyIcon
|
||
color={theme.palette.brightPurple.main}
|
||
bgcolor={"#EEE4FC"}
|
||
/>
|
||
</IconButton>
|
||
</Box>
|
||
</Box>
|
||
|
||
<Box sx={{ display: "inline-flex", margin: "11px 0 0 0", alignItems: "flex-end" }}>
|
||
<Tooltip title={!canSave ? (utmHasSpaces ? "уберите пробелы" : "заполните поля") : ""} disableHoverListener={canSave} arrow>
|
||
<span style={{ marginLeft: "auto" }}>
|
||
<Button variant="contained" disabled={!canSave} onClick={handleSave} sx={{ width: "133px", height: "44px" }}>Сохранить</Button>
|
||
</span>
|
||
</Tooltip>
|
||
</Box>
|
||
|
||
{quiz?.qid && (
|
||
<UtmList
|
||
items={filteredItems}
|
||
loading={loading}
|
||
error={error}
|
||
quizQid={quiz.qid}
|
||
onDelete={async (id) => {
|
||
const prev = items;
|
||
setItems(prev.filter((it) => it.id !== id));
|
||
const [, err] = await utmApi.softDelete(id);
|
||
if (err) setItems(prev);
|
||
}}
|
||
/>)}
|
||
</Paper>
|
||
);
|
||
|
||
} |