удаление без реактивности, выведен корректный список линков

This commit is contained in:
Nastya 2025-06-02 00:27:22 +03:00
parent c2d79c04cc
commit e7121cb06a
7 changed files with 212 additions and 144 deletions

@ -47,13 +47,13 @@ export interface AuditoryAddParams {
}
// API calls
export const auditoryGet = async ({ quizId }: AuditoryGetParams): Promise<[AuditoryResponse | null, string?]> => {
export const auditoryGet = async ({ quizId }: AuditoryGetParams): Promise<[AuditoryItem[] | null, string?]> => {
if (!quizId) {
return [null, "Quiz ID is required"];
}
try {
const response = await makeRequest<AuditoryGetRequest, AuditoryResponse>({
const response = await makeRequest<AuditoryGetRequest, AuditoryItem[]>({
url: `${API_URL}/quiz/${quizId}/auditory`,
method: "GET",
});
@ -72,10 +72,7 @@ export const auditoryDelete = async ({ quizId, auditoryId }: AuditoryDeleteParam
try {
const response = await makeRequest<AuditoryDeleteRequest, AuditoryResponse>({
url: `${API_URL}/quiz/${quizId}/auditory`,
body: {
id: auditoryId
},
url: `${API_URL}/quiz/${quizId}/auditory/${auditoryId}`,
method: "DELETE",
});

@ -0,0 +1,74 @@
import { AuditoryItem } from "@/api/auditory";
import CopyIcon from "@/assets/icons/CopyIcon";
import Trash from "@/assets/icons/trash";
import { InfoPopover } from "@/ui_kit/InfoPopover";
import TooltipClickInfo from "@/ui_kit/Toolbars/TooltipClickInfo";
import { useDomainDefine } from "@/utils/hooks/useDomainDefine";
import { IconButton, ListItem, Typography, useTheme } from "@mui/material";
interface AuditoryLinkProps {
item: AuditoryItem;
index: number;
deleteModal: (id:number) => void
}
export const AuditoryLink = ({ item, index, deleteModal }: AuditoryLinkProps) => {
const theme = useTheme();
const { isTestServer } = useDomainDefine();
const handleCopy = (text: string) => {
navigator.clipboard.writeText(text);
};
const linkText = `${isTestServer ? "https://s.hbpn.link/" : "https://hbpn.link/"}?_paud=${item.id}`;
return (
<ListItem
key={index}
disablePadding
sx={{
bgcolor: "#F2F3F7",
borderRadius: "10px",
p: "13px 14px 13px 20px",
mb: "8px",
maxWidth: "756px",
display: "flex",
alignItems: "center",
transition: 'background 0.2s, border 0.2s',
'& .MuiListItemSecondaryAction-root': {
display: 'flex',
alignItems: 'center',
gap: '12px',
width: "70px",
justifyContent: "space-between",
},
}}
secondaryAction={
<>
<IconButton onClick={() => deleteModal(item.id)} edge="end" aria-label="info" sx={{ color: theme.palette.brightPurple.main, p: 0, width: 18, height: 18 }}>
<Trash sx={{
"& path": {
stroke: theme.palette.brightPurple.main,
}
}} />
</IconButton>
<IconButton edge="end" aria-label="info" sx={{ color: theme.palette.brightPurple.main, p: 0, width: 18, height: 18 }}>
<TooltipClickInfo title={`Возраст: ${item.age}\n Пол: ${item.sex ? "мужской" : "женский"}`} />
</IconButton>
<IconButton
edge="end"
aria-label="copy"
sx={{ color: theme.palette.brightPurple.main, p: 0, width: 18, height: 18, marginRight: "-2px" }}
onClick={() => handleCopy(linkText)}
>
<CopyIcon color={theme.palette.brightPurple.main} />
</IconButton>
</>
}
>
<Typography sx={{ color: 'black', fontWeight: 400, fontSize: "16px" }}>
{linkText}
</Typography>
</ListItem>
);
};

@ -1,37 +1,17 @@
import { auditoryGet, AuditoryResponse, AuditoryItem } from "@/api/auditory";
import ArrowDownIcon from "@/assets/icons/ArrowDownIcon";
import CopyIcon from "@/assets/icons/CopyIcon";
import { useCurrentQuiz } from "@/stores/quizes/hooks";
import { InfoPopover } from "@/ui_kit/InfoPopover";
import { useDomainDefine } from "@/utils/hooks/useDomainDefine";
import { Box, Collapse, IconButton, List, ListItem, Typography, useTheme } from "@mui/material";
import { Box, Collapse, IconButton, List, Typography, useTheme } from "@mui/material";
import { useEffect, useState } from "react";
import { AuditoryLink } from "./AuditoryLink";
const PURPLE = "#7E2AEA";
export const AuditoryList = () => {
export const AuditoryList = ({auditory, deleteModal}:{auditory:AuditoryItem[], deleteModal:(id:number) => void}) => {
const theme = useTheme();
const quiz = useCurrentQuiz();
const { isTestServer } = useDomainDefine();
const [linksOpen, setLinksOpen] = useState(true);
const [auditory, setAuditory] = useState<AuditoryItem[]>([]);
const handleCopy = (text: string) => {
navigator.clipboard.writeText(text);
};
useEffect(() => {
(async () => {
if (quiz?.backendId) {
const [result, error] = await auditoryGet({ quizId: quiz.backendId });
console.log("result-___---_------__---__-__---_------__---__-__---_------__---__-__---_------__---__-____--__")
console.log(result)
if (result) {
setAuditory(result);
}
}
})();
}, [quiz]);
console.log("auditory-___---_auditory__---__-__auditory_------__---__-__---_------__---__-__---_------__---__-____--__")
console.log(auditory)
@ -51,7 +31,7 @@ export const AuditoryList = () => {
Ваши сохраненные ссылки
</Typography>
<IconButton
sx={{ cursor: 'pointer', color: PURPLE, display: 'flex', alignItems: 'center', transition: 'transform 0.2s', transform: linksOpen ? 'rotate(0deg)' : 'rotate(180deg)' }}
sx={{ cursor: 'pointer', color: theme.palette.brightPurple.main, display: 'flex', alignItems: 'center', transition: 'transform 0.2s', transform: linksOpen ? 'rotate(0deg)' : 'rotate(180deg)' }}
onClick={() => setLinksOpen((prev) => !prev)}
size="large"
>
@ -60,52 +40,9 @@ export const AuditoryList = () => {
</Box>
<Collapse in={linksOpen} timeout="auto" unmountOnExit sx={{ mt: "3px" }}>
<List sx={{ gap: '8px', p: 0, m: 0 }}>
{auditory.map((item, idx) => {
const linkText = `${isTestServer ? "https://s.hbpn.link/" : "https://hbpn.link/"}?_paud=${item.id}`;
console.log(item)
return (
<ListItem
key={idx}
disablePadding
sx={{
bgcolor: "#F2F3F7",
borderRadius: "10px",
p: "13px 14px 13px 20px",
mb: "8px",
maxWidth: "756px",
display: "flex",
alignItems: "center",
transition: 'background 0.2s, border 0.2s',
'& .MuiListItemSecondaryAction-root': {
display: 'flex',
alignItems: 'center',
gap: '12px',
width: "60px",
justifyContent: "space-between",
},
}}
secondaryAction={
<>
<IconButton edge="end" aria-label="info" sx={{ color: PURPLE, p: 0, width: 18, height: 18 }}>
<InfoPopover />
</IconButton>
<IconButton
edge="end"
aria-label="copy"
sx={{ color: PURPLE, p: 0, width: 18, height: 18, marginRight: "-2px" }}
onClick={() => handleCopy(linkText)}
>
<CopyIcon color={PURPLE} />
</IconButton>
</>
}
>
<Typography sx={{ color: 'black', fontWeight: 400, fontSize: "16px" }}>
{linkText}
</Typography>
</ListItem>
);
})}
{auditory.map((item, idx) => (
<AuditoryLink deleteModal={deleteModal} key={idx} item={item} index={idx} />
))}
</List>
</Collapse>
</Box>

@ -1,4 +1,4 @@
import { Box, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio, Select, MenuItem, useTheme } from "@mui/material";
import { Box, FormControl, FormLabel, RadioGroup, FormControlLabel, Radio, Select, MenuItem, useTheme, Button } from "@mui/material";
import { InfoPopover } from '@ui_kit/InfoPopover';
import CheckboxIcon from "@icons/Checkbox";
@ -7,6 +7,7 @@ interface GenderAndAgeSelectorProps {
setGender: (gender: string) => void;
}
export default function GenderAndAgeSelector({ gender, setGender }: GenderAndAgeSelectorProps) {
const theme = useTheme();
return (
@ -167,6 +168,21 @@ export default function GenderAndAgeSelector({ gender, setGender }: GenderAndAge
</Select>
</FormControl>
<Button
variant="contained"
sx={{
bgcolor: theme.palette.brightPurple.main,
borderRadius: "8px",
width: "130px",
height: "48px",
boxShadow: "none",
textTransform: "none",
fontSize: "18px",
'&:hover': { bgcolor: theme.palette.brightPurple.main },
}}
>
Ок
</Button>
</Box>
);
}

@ -1,4 +1,4 @@
import { Box, Container, Typography, TextField, Button, List, ListItem, IconButton } from "@mui/material";
import { Box, Container, Typography, TextField, Button, List, ListItem, IconButton, Modal } from "@mui/material";
import { InfoPopover } from '@ui_kit/InfoPopover';
import CopyIcon from "@/assets/icons/CopyIcon";
import GenderAndAgeSelector from "./GenderAndAgeSelector";
@ -7,17 +7,38 @@ import CustomTextField from "@ui_kit/CustomTextField";
import Collapse from '@mui/material/Collapse';
import { ArrowDownIcon } from "../../assets/icons/questionsPage/ArrowDownIcon";
import { useTheme } from "@mui/material";
import { auditoryAdd, auditoryGet } from "@/api/auditory";
import { AuditoryItem, auditoryAdd, auditoryDelete, auditoryGet } from "@/api/auditory";
import { useCurrentQuiz } from "@/stores/quizes/hooks";
import { AuditoryList } from "./AuditoryList";
const PURPLE = "#7E2AEA";
export default function PersonalizationAI() {
const theme = useTheme();
const [gender, setGender] = useState('');
const [auditory, setAuditory] = useState<AuditoryItem[]>([]);
const [deleteModal, setDeleteModal] = useState<number>(0);
const quiz = useCurrentQuiz();
useEffect(() => {
(async () => {
if (quiz?.backendId) {
const [result, error] = await auditoryGet({ quizId: quiz.backendId });
console.log("result-___---_------__---__-__---_------__---__-__---_------__---__-__---_------__---__-____--__")
console.log(result)
if (result) {
setAuditory(result);
}
}
})();
}, [quiz]);
const handleDelete = () => {
setDeleteModal(0)
auditoryDelete({ quizId: quiz?.backendId, auditoryId: deleteModal })
}
return (
<>
<Container id="PersonalizationAI" maxWidth={false} sx={{ minHeight: "100%", p: "20px" }}>
<Typography variant="h5" color={theme.palette.grey3.main} fontWeight={700} sx={{ fontSize: 24, letterSpacing: "-0.2px" }}>
Персонализация вопросов с помощью AI
@ -60,7 +81,6 @@ export default function PersonalizationAI() {
>
Вставьте ссылку со всеми utm-метками
</Typography>
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mt: "6px" }}>
<CustomTextField
placeholder="linkexample.com"
maxLength={5}
@ -69,27 +89,41 @@ export default function PersonalizationAI() {
}}
/>
<Button
variant="contained"
sx={{
bgcolor: PURPLE,
borderRadius: "8px",
width: "130px",
height: "48px",
boxShadow: "none",
textTransform: "none",
fontSize: "18px",
'&:hover': { bgcolor: PURPLE },
}}
>
Ок
</Button>
</Box>
</Box>
</Box>
<AuditoryList />
<AuditoryList deleteModal={setDeleteModal} auditory={auditory} />
</Container>
<Modal
open={Boolean(deleteModal)}
onClose={() => setDeleteModal(0)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box
sx={{
position: "absolute" as "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
maxWidth: "620px",
width: "100%",
bgcolor: "background.paper",
borderRadius: "12px",
boxShadow: 24,
p: "20px",
display: "flex",
flexDirection: "column",
alignItems: "center"
}}
>
<Typography sx={{ width: "100%" ,textAlign: "center", mb: "25px"}}>Уверены, что хотите удалить ссылку?</Typography>
<Button sx={{mb: "20px"}} onClick={handleDelete}>Удалить</Button>
<Button variant="contained" onClick={() => setDeleteModal(0)} >Отмена</Button>
</Box>
</Modal>
</>
);
}

@ -1,9 +1,17 @@
import { useState, MouseEvent } from "react";
import { useState, MouseEvent, ReactNode } from "react";
import Info from "@icons/Info";
import { Paper, Popover, SxProps, Typography } from "@mui/material";
export const InfoPopover = ({ blink = false, sx }: {blink?: boolean, sx?: SxProps}) => {
export const InfoPopover = ({
blink = false,
sx,
children = "подсказка"
}: {
blink?: boolean,
sx?: SxProps,
children?: ReactNode
}) => {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
@ -43,9 +51,7 @@ export const InfoPopover = ({ blink = false, sx }: {blink?: boolean, sx?: SxProp
flexDirection: "column",
}}
>
<Typography>
подсказка
</Typography>
{children}
</Paper>
</Popover>
</>

@ -26,6 +26,10 @@ export default function TooltipClickInfo({ title }: { title: string }) {
disableFocusListener
disableHoverListener
disableTouchListener
sx={{
fontSize: "12px",
p:"10px"
}}
title={title}
>
<IconButton onClick={handleTooltipOpen}>