Compare commits
No commits in common. "main" and "statistics_bugs" have entirely different histories.
main
...
statistics
@ -6,32 +6,20 @@ on:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
# CreateImage:
|
||||
# runs-on: [skeris]
|
||||
# uses: https://gitea.pena/PenaDevops/actions.git/.gitea/workflows/build-image.yml@v1.1.6-p
|
||||
# with:
|
||||
# runner: skeris
|
||||
# secrets:
|
||||
# REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
# REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
# DeployService:
|
||||
# runs-on: [frontprod]
|
||||
# needs: CreateImage
|
||||
# uses: https://gitea.pena/PenaDevops/actions.git/.gitea/workflows/deploy.yml@v1.1.4-p7
|
||||
# with:
|
||||
# runner: hubprod
|
||||
# actionid: ${{ gitea.run_id }}
|
||||
#
|
||||
CreateImage:
|
||||
runs-on: [skeris]
|
||||
uses: https://gitea.pena/PenaDevops/actions.git/.gitea/workflows/build-image.yml@v1.1.6-p
|
||||
with:
|
||||
runner: skeris
|
||||
secrets:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
DeployService:
|
||||
if: contains(github.event.package.name, 'main')
|
||||
runs-on: [frontprod]
|
||||
container:
|
||||
image: gitea.pena/penadevops/container-images/node-compose:main
|
||||
env:
|
||||
GITHUB_RUN_NUMBER: "${{ inputs.actionid }}"
|
||||
volumes:
|
||||
- /run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: http://gitea.pena/PenaDevops/actions.git/checkout@v1
|
||||
- run: compose -f deployments/main/docker-compose.yaml up -d
|
||||
needs: CreateImage
|
||||
uses: https://gitea.pena/PenaDevops/actions.git/.gitea/workflows/deploy.yml@v1.1.4-p7
|
||||
with:
|
||||
runner: hubprod
|
||||
actionid: ${{ gitea.run_id }}
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@ services:
|
||||
squiz:
|
||||
container_name: squiz
|
||||
restart: unless-stopped
|
||||
image: gitea.pena/squiz/frontpanel/main:latest
|
||||
image: gitea.pena/squiz/frontpanel/main:$GITHUB_RUN_NUMBER
|
||||
hostname: squiz
|
||||
tty: true
|
||||
pull_policy: always
|
||||
|
2669
package-lock.json
generated
2669
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -89,10 +89,12 @@
|
||||
"devDependencies": {
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@emoji-mart/react": "^1.1.1",
|
||||
"@types/cypress": "^1.1.6",
|
||||
"@types/cytoscape-popper": "^2.0.4",
|
||||
"@types/react-beautiful-dnd": "^13.1.4",
|
||||
"@types/react-cytoscapejs": "^1.2.4",
|
||||
"craco-alias": "^3.0.1",
|
||||
"cypress": "^14.4.1",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^3.1.1"
|
||||
|
42
src/App.tsx
42
src/App.tsx
@ -277,10 +277,31 @@ export default function App() {
|
||||
path="/gallery"
|
||||
element={<LazyLoading children={<QuizGallery />} />}
|
||||
/>
|
||||
<Route
|
||||
path="/list"
|
||||
element={
|
||||
<LazyLoading
|
||||
children={<MyQuizzesFull />}
|
||||
fallback={<ListPageDummy />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={"/view/:quizId"}
|
||||
element={<LazyLoading children={<ViewPage />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/tariffs"}
|
||||
element={<LazyLoading children={<Tariffs />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/analytics"}
|
||||
element={<LazyLoading children={<Analytics />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/results/:quizId"}
|
||||
element={<LazyLoading children={<QuizAnswersPage />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/qaz"}
|
||||
element={<LazyLoading children={<InfoPrivilege />} />}
|
||||
@ -312,27 +333,6 @@ export default function App() {
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<Route
|
||||
path="/list"
|
||||
element={
|
||||
<LazyLoading
|
||||
children={<MyQuizzesFull />}
|
||||
fallback={<ListPageDummy />}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={"/view/:quizId"}
|
||||
element={<LazyLoading children={<ViewPage />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/analytics"}
|
||||
element={<LazyLoading children={<Analytics />} />}
|
||||
/>
|
||||
<Route
|
||||
path={"/results/:quizId"}
|
||||
element={<LazyLoading children={<QuizAnswersPage />} />}
|
||||
/>
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { Box, SxProps } from "@mui/material";
|
||||
import PostbackDefault from "./Postback";
|
||||
import PostbackPC from "./PostbackPC";
|
||||
|
||||
export const PostbackLogo = (sx: SxProps) => (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1, ...sx }}>
|
||||
<PostbackPC sx={{ width: "24px", height: "20px" }} />
|
||||
<PostbackDefault sx={{ width: "40px", height: "8px" }} />
|
||||
</Box>
|
||||
);
|
@ -1,15 +0,0 @@
|
||||
import { Box, SxProps } from "@mui/material";
|
||||
|
||||
export const ZapierLogo = (sx: SxProps) => (
|
||||
<Box
|
||||
component="img"
|
||||
src="/src/assets/icons/logo/zapier.png"
|
||||
alt="Zapier"
|
||||
sx={{
|
||||
width: "40px",
|
||||
height: "40px",
|
||||
objectFit: "contain",
|
||||
...sx,
|
||||
}}
|
||||
/>
|
||||
);
|
@ -18,7 +18,6 @@ import { ReactComponent as NextIcon } from "@icons/Analytics/next.svg";
|
||||
import { ReactComponent as LeftArrowIcon } from "@icons/Analytics/leftArrow.svg";
|
||||
import { ReactComponent as RightArrowIcon } from "@icons/Analytics/rightArrow.svg";
|
||||
import { extractOrder } from "@utils/extractOrder";
|
||||
import { parseTitle } from "../utils/parseTitle";
|
||||
|
||||
type AnswerProps = {
|
||||
title: string;
|
||||
@ -38,9 +37,7 @@ type PaginationProps = {
|
||||
|
||||
const Answer = ({ title, percent, highlight }: AnswerProps) => {
|
||||
const theme = useTheme();
|
||||
const parsedTitle = parseTitle(title);
|
||||
|
||||
console.log("parsedTitle: " + parsedTitle);
|
||||
return (
|
||||
<Box sx={{ padding: "15px 25px" }}>
|
||||
<Box
|
||||
@ -54,7 +51,7 @@ const Answer = ({ title, percent, highlight }: AnswerProps) => {
|
||||
>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
title={parsedTitle}
|
||||
title={title}
|
||||
value={percent}
|
||||
sx={{
|
||||
width: "100%",
|
||||
@ -64,7 +61,7 @@ const Answer = ({ title, percent, highlight }: AnswerProps) => {
|
||||
border: `1px solid ${highlight ? theme.palette.brightPurple.main : theme.palette.grey2.main}`,
|
||||
"& > span": { background: highlight ? "#D9C0F9" : "#9A9AAF1A" },
|
||||
"&::before": {
|
||||
content: parsedTitle ? `"${parsedTitle}"` : `"Без имени"`,
|
||||
content: title ? `"${title}"` : `"Без имени"`,
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
left: "20px",
|
||||
@ -280,12 +277,12 @@ export const Answers: FC<AnswersProps> = ({ data }) => {
|
||||
? ` ${currentAnswer?.[0]}`
|
||||
: "Без заголовка"}
|
||||
</Typography>
|
||||
{/* <ButtonBase>
|
||||
<ButtonBase>
|
||||
<DoubleCheckIcon />
|
||||
</ButtonBase>
|
||||
<ButtonBase>
|
||||
<NextIcon />
|
||||
</ButtonBase> */}
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
{currentAnswerExtended.map(([title, percent], index) => (
|
||||
<Answer
|
||||
|
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* Парсит title и извлекает Description из JSON строки
|
||||
* @param title - строка, которая может быть JSON с Image и Description
|
||||
* @returns значение Description или "нет названия" если парсинг не удался
|
||||
*/
|
||||
export const parseTitle = (title: string): string => {
|
||||
if (!title || typeof title !== 'string') {
|
||||
return "нет названия";
|
||||
}
|
||||
|
||||
// Убираем обратные кавычки если они есть
|
||||
const cleanTitle = title.replace(/^`|`$/g, '');
|
||||
|
||||
// Проверяем, начинается ли строка с { и заканчивается на }
|
||||
if (!cleanTitle.trim().startsWith('{') || !cleanTitle.trim().endsWith('}')) {
|
||||
return title;
|
||||
}
|
||||
|
||||
try {
|
||||
// Пытаемся распарсить как JSON
|
||||
const parsed = JSON.parse(cleanTitle);
|
||||
console.log("parsed object:", parsed);
|
||||
console.log("parsed.Image:", parsed.Image);
|
||||
console.log("parsed.Description:", parsed.Description);
|
||||
|
||||
// Проверяем, что это объект с полями Image и Description (специфичный для вопросов типа images и varimg)
|
||||
if (parsed &&
|
||||
typeof parsed === 'object' &&
|
||||
'Image' in parsed &&
|
||||
'Description' in parsed) {
|
||||
console.log("Returning Description:", parsed.Description);
|
||||
return parsed.Description || "нет названия";
|
||||
}
|
||||
|
||||
// Если это не объект с Image и Description, возвращаем исходную строку
|
||||
console.log("Not Image/Description object, returning original title");
|
||||
return title;
|
||||
} catch (error) {
|
||||
// Если парсинг не удался, возвращаем исходную строку
|
||||
console.log("JSON parse error, returning original title");
|
||||
return title;
|
||||
}
|
||||
};
|
@ -132,7 +132,7 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* <Typography variant="h6" sx={sectionTitleStyles}>
|
||||
<Typography variant="h6" sx={sectionTitleStyles}>
|
||||
Автоматизация
|
||||
</Typography>
|
||||
<Box sx={containerStyles}>
|
||||
@ -144,7 +144,7 @@ export const PartnersBoard: FC<PartnersBoardProps> = ({
|
||||
setIsModalOpen={setIsPostbackModalOpen}
|
||||
setCompanyName={setCompanyName}
|
||||
/>
|
||||
</Box> */}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{companyName && (
|
||||
|
@ -26,12 +26,6 @@ export default function Component() {
|
||||
const userId = useUserStore((state) => state.userId);
|
||||
const location = useLocation();
|
||||
|
||||
console.log("HeaderLanding debug:", {
|
||||
userId,
|
||||
location: location.pathname,
|
||||
backgroundLocation: location.state?.backgroundLocation
|
||||
});
|
||||
|
||||
return (
|
||||
<SectionStyled
|
||||
tag={"header"}
|
||||
@ -83,7 +77,6 @@ export default function Component() {
|
||||
to={"/signin"}
|
||||
state={{ backgroundLocation: location }}
|
||||
variant="outlined"
|
||||
onClick={() => console.log("Signin button clicked")}
|
||||
sx={{
|
||||
color: "black",
|
||||
border: "1px solid black",
|
||||
|
@ -23,12 +23,10 @@ type SettingTextFieldProps = {
|
||||
questionId: string;
|
||||
isRequired: boolean;
|
||||
isAutofill: boolean;
|
||||
multi: boolean;
|
||||
detailedAnswer: boolean;
|
||||
answerType: Answer;
|
||||
};
|
||||
|
||||
const SettingTextField = memo<SettingTextFieldProps>(function ({ questionId, isRequired, detailedAnswer, multi, isAutofill, answerType }) {
|
||||
const SettingTextField = memo<SettingTextFieldProps>(function ({ questionId, isRequired, isAutofill, answerType }) {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(790));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(900));
|
||||
@ -147,21 +145,6 @@ const SettingTextField = memo<SettingTextFieldProps>(function ({ questionId, isR
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<CustomCheckbox
|
||||
dataCy="checkbox-optional-question"
|
||||
sx={{
|
||||
display: isMobile ? "flex" : "block",
|
||||
mr: isMobile ? "0px" : "16px",
|
||||
alignItems: isMobile ? "flex-end" : "center",
|
||||
}}
|
||||
label={"Многострочный ответ"}
|
||||
checked={multi}
|
||||
handleChange={(e) => {
|
||||
updateQuestion<QuizQuestionText>(questionId, (question) => {
|
||||
question.content.multi = e.target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -17,8 +17,6 @@ export default function SwitchTextField({ switchState = "setting", question }: P
|
||||
isRequired={question.content.required}
|
||||
isAutofill={question.content.autofill}
|
||||
answerType={question.content.answerType}
|
||||
detailedAnswer={Boolean(question.content?.detailedAnswer)}
|
||||
multi={Boolean(question.content?.multi)}
|
||||
/>
|
||||
);
|
||||
case "image":
|
||||
|
@ -388,7 +388,7 @@ export const CardAnswer: FC<CardAnswerProps> = ({
|
||||
</>
|
||||
)}
|
||||
{!(typeOuestion === "file" || typeOuestion === "images" || typeOuestion === "varimg") && (
|
||||
<Typography sx={{ fontSize: "18px", whiteSpace: "pre-line" }}>{answer.content}</Typography>
|
||||
<Typography sx={{ fontSize: "18px" }}>{answer.content}</Typography>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
|
@ -22,7 +22,6 @@ import { makeRequest } from "@frontend/kitui";
|
||||
import { setAuthToken } from "@frontend/kitui";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
import { recoverUser } from "@api/user";
|
||||
import PasswordInput from "@/ui_kit/passwordInput";
|
||||
interface Values {
|
||||
password: string;
|
||||
}
|
||||
@ -55,8 +54,6 @@ export default function RecoverPassword() {
|
||||
initialValues,
|
||||
validationSchema,
|
||||
onSubmit: async (values, formikHelpers) => {
|
||||
console.log("tokenUser", tokenUser);
|
||||
|
||||
if (tokenUser) {
|
||||
setAuthToken(tokenUser || "");
|
||||
const [_, recoverError] = await recoverUser(values.password);
|
||||
@ -77,12 +74,9 @@ export default function RecoverPassword() {
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
console.log("RecoverPassword useEffect - window.location.search:", window.location.search);
|
||||
console.log("RecoverPassword useEffect - window.location.href:", window.location.href);
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const authToken = params.get("auth");
|
||||
console.log("RecoverPassword useEffect - authToken:", authToken);
|
||||
setTokenUser(authToken || "");
|
||||
setTokenUser(authToken);
|
||||
|
||||
history.pushState(null, document.title, "/changepwd");
|
||||
return () => {
|
||||
@ -161,15 +155,13 @@ export default function RecoverPassword() {
|
||||
>
|
||||
Введите новый пароль
|
||||
</Typography>
|
||||
<PasswordInput
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
value: formik.values.password,
|
||||
placeholder: "Не менее 8 символов",
|
||||
placeholder: "введите пароль",
|
||||
onBlur: formik.handleBlur,
|
||||
error: formik.touched.password && Boolean(formik.errors.password),
|
||||
helperText: formik.touched.password && formik.errors.password,
|
||||
type: "password",
|
||||
"data-cy": "password",
|
||||
}}
|
||||
onChange={formik.handleChange}
|
||||
color="#F2F3F7"
|
||||
|
@ -875,24 +875,6 @@ export default function StartPageSettings() {
|
||||
Включить защиту от копирования
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", gap: "20px", alignItems: "center", mt: "20px" }}>
|
||||
<CustomizedSwitch
|
||||
checked={quiz.config?.backBlocked}
|
||||
onChange={(e) => {
|
||||
updateQuiz(quiz.id, (quiz) => {
|
||||
quiz.config.backBlocked = e.target.checked;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
color: theme.palette.grey3.main,
|
||||
}}
|
||||
>
|
||||
Запретить шаг назад
|
||||
</Typography>
|
||||
</Box>
|
||||
{!isSmallMonitor &&<SwitchAI />}
|
||||
</>
|
||||
)}
|
||||
|
@ -66,18 +66,7 @@ export const setUser = (user: User) =>
|
||||
}),
|
||||
);
|
||||
|
||||
export const clearUserData = () => {
|
||||
console.log("clearUserData: Clearing user data");
|
||||
console.log("clearUserData: Before clearing -", useUserStore.getState());
|
||||
|
||||
useUserStore.setState({ ...initialState });
|
||||
|
||||
console.log("clearUserData: After clearing -", useUserStore.getState());
|
||||
|
||||
// Также очищаем localStorage напрямую
|
||||
localStorage.removeItem("user");
|
||||
console.log("clearUserData: localStorage cleared");
|
||||
};
|
||||
export const clearUserData = () => useUserStore.setState({ ...initialState });
|
||||
|
||||
export const setUserAccount = (userAccount: OriginalUserAccount) =>
|
||||
useUserStore.setState({ userAccount });
|
||||
|
@ -1,31 +1,8 @@
|
||||
import { useUserStore } from "@root/user";
|
||||
import { Navigate, Outlet } from "react-router-dom";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function PrivateRoute() {
|
||||
const user = useUserStore((state) => state.user);
|
||||
const userId = useUserStore((state) => state.userId);
|
||||
|
||||
console.log("PrivateRoute debug:", {
|
||||
user: user ? "exists" : "null",
|
||||
userId: user?._id,
|
||||
userIdFromStore: userId,
|
||||
currentPath: window.location.pathname,
|
||||
userObject: user
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
console.log("PrivateRoute: User is null, redirecting to / via useEffect");
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
if (!user) {
|
||||
console.log("PrivateRoute: User is null, showing fallback");
|
||||
return <></>;
|
||||
}
|
||||
|
||||
console.log("PrivateRoute: User exists, rendering Outlet");
|
||||
return <Outlet />;
|
||||
return user ? <Outlet /> : <Navigate to="/" replace />;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { cartApi } from "@api/cart";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate, useSearchParams, useLocation } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { calcTimeOfReadyPayCart, cancelPayCartProcess, startPayCartProcess, useNotEnoughMoneyAmount } from "@/stores/notEnoughMoneyAmount";
|
||||
import { startCC } from "@/stores/cc";
|
||||
import { setEditQuizId, setCurrentStep } from "@root/quizes/actions";
|
||||
@ -17,7 +17,6 @@ import { setEditQuizId, setCurrentStep } from "@root/quizes/actions";
|
||||
export const useAfterPay = () => {
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
|
||||
const userId = useUserStore(store => store.userId)
|
||||
const userAccount = useUserStore(state => state.userAccount);
|
||||
@ -29,28 +28,8 @@ export const useAfterPay = () => {
|
||||
let URLadditionalinformation = searchParams.get("additionalinformation");//его токен
|
||||
|
||||
useEffect(() => {
|
||||
// Проверяем, открыта ли модалка восстановления пароля
|
||||
const isRecoverPasswordModal = location.state?.backgroundLocation?.pathname === "/changepwd";
|
||||
|
||||
// Проверяем, есть ли токен восстановления пароля в URL
|
||||
const hasAuthToken = searchParams.get("auth") || window.location.search.includes("auth=");
|
||||
|
||||
console.log("useAfterPay debug:", {
|
||||
pathname: location.pathname,
|
||||
backgroundLocation: location.state?.backgroundLocation,
|
||||
isRecoverPasswordModal,
|
||||
searchParams: window.location.search,
|
||||
authToken: searchParams.get("auth"),
|
||||
hasAuthToken
|
||||
});
|
||||
|
||||
// НЕ очищаем параметры на странице восстановления пароля, когда открыты модалки или есть токен auth
|
||||
if (location.pathname !== "/changepwd" && !location.state?.backgroundLocation && !isRecoverPasswordModal && !hasAuthToken) {
|
||||
console.log("Очищаем параметры URL");
|
||||
setSearchParams({}, { replace: true });
|
||||
} else {
|
||||
console.log("НЕ очищаем параметры URL");
|
||||
}
|
||||
|
||||
if (userId && URLuserId && userId === URLuserId) {
|
||||
|
||||
@ -99,7 +78,7 @@ export const useAfterPay = () => {
|
||||
}
|
||||
|
||||
}
|
||||
}, [location.pathname, location.state?.backgroundLocation]);
|
||||
}, []);
|
||||
|
||||
//Обработка необходимости купить после пополнения
|
||||
useEffect(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user