diff --git a/src/api/leadtarget.ts b/src/api/leadtarget.ts new file mode 100644 index 00000000..c05c4923 --- /dev/null +++ b/src/api/leadtarget.ts @@ -0,0 +1,101 @@ +import { makeRequest } from "@frontend/kitui"; +import { parseAxiosError } from "@utils/parse-error"; + +export type LeadTargetType = "mail" | "telegram" | "whatsapp" | "webhook"; + +export interface LeadTargetModel { + ID: number; + AccountID: string; + Type: LeadTargetType; + QuizID: number; + Target: string; + InviteLink?: string; + Deleted?: boolean; + CreatedAt?: string; +} + +const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`; + +export const getLeadTargetsByQuiz = async ( + quizId: number, +): Promise<[LeadTargetModel[] | null, string?]> => { + try { + const items = await makeRequest({ + method: "GET", + url: `${API_URL}/account/leadtarget/${quizId}`, + }); + + return [items]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + return [null, `Не удалось получить цели лида. ${error}`]; + } +}; + +export const createLeadTarget = async ( + body: { + type: LeadTargetType; + quizID: number; + target: string; + name?: string; + }, +): Promise<[LeadTargetModel | true | null, string?]> => { + try { + const response = await makeRequest({ + method: "POST", + url: `${API_URL}/account/leadtarget`, + body, + }); + + return [response]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + return [null, `Не удалось создать цель лида. ${error}`]; + } +}; + +export const updateLeadTarget = async ( + body: { + id: number; + target: string; + }, +): Promise<[LeadTargetModel | null, string?]> => { + try { + const updated = await makeRequest({ + method: "PUT", + url: `${API_URL}/account/leadtarget`, + body, + }); + + return [updated]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + return [null, `Не удалось обновить цель лида. ${error}`]; + } +}; + +export const deleteLeadTarget = async ( + id: number, +): Promise<[true | null, string?]> => { + try { + await makeRequest({ + method: "DELETE", + url: `${API_URL}/account/leadtarget/${id}`, + }); + + return [true]; + } catch (nativeError) { + const [error] = parseAxiosError(nativeError); + return [null, `Не удалось удалить цель лида. ${error}`]; + } +}; + +export const leadTargetApi = { + getByQuiz: getLeadTargetsByQuiz, + create: createLeadTarget, + update: updateLeadTarget, + delete: deleteLeadTarget, +}; + + + diff --git a/src/assets/icons/OrangeYoutube.tsx b/src/assets/icons/OrangeYoutube.tsx new file mode 100755 index 00000000..42524f75 --- /dev/null +++ b/src/assets/icons/OrangeYoutube.tsx @@ -0,0 +1,13 @@ +import { Box, SxProps } from "@mui/material"; + +export default function OrangeYoutube(sx: SxProps) { + return ( + + + + + + ); +} diff --git a/src/openapi (1).yaml b/src/openapi (1).yaml new file mode 100644 index 00000000..87d5dae2 --- /dev/null +++ b/src/openapi (1).yaml @@ -0,0 +1,2204 @@ +openapi: 3.0.3 +info: + title: QUIZ service api + description: Title + version: 1.0.0 +components: + requestBodies: + GetQuizResultsReq: + content: + 'application/json': + schema: + type: object + properties: + to: + description: таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет. верхняя граница времени + type: integer + from: + description: таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет. нижняя граница времени + new: + description: флаг, по которому вернутся только новые результаты, ещё не просмотренные пользователем + type: boolean + page: + description: номер страницы для пагинации + type: integer + require: true + limit: + description: размер страницы для пагинации + type: integer + require: true + QuizCreateReq: + content: + 'application/json': + schema: + type: object + properties: + fingerprinting: + description: set true for save deviceId + type: boolean + repeatable: + description: set true for allow user to repeat quiz + type: boolean + note_prevented: + description: set true for save statistic of incomplete quiz passing + type: boolean + mail_notifications: + description: set true for mail notification for each quiz passing + type: boolean + unique_answers: + description: set true for save statistics only for unique quiz passing + type: boolean + name: + description: name of quiz. max 280 length + type: string + description: + description: description of quiz + type: string + config: + description: config of quiz. serialized json for rules of quiz flow + type: string + status: + description: status of quiz. allow only '', 'draft', 'template', 'stop', 'start' + type: string + limit: + description: limit is count of max quiz passing + type: integer + due_to: + description: last time when quiz is valid. timestamp in seconds + type: integer + time_of_passing: + description: seconds to pass quiz + type: integer + pausable: + description: true if it is allowed for pause quiz + type: boolean + question_cnt: + description: count of questions + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuizGetListReq: + content: + 'application/json': + schema: + type: object + properties: + limit: + description: max items on page + type: integer + offset: + description: page number + type: integer + from: + description: start time of time period. timestamp in seconds + type: integer + to: + description: end time of time period. timestamp in seconds + type: integer + search: + description: string for fulltext search in titles of quizes + type: string + status: + description: allow only - draft, template, timeout, stop, start, offlimit + type: string + deleted: + description: get deleted quizes + type: boolean + archived: + description: get archived quizes + type: boolean + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuestionCreateReq: + content: + 'application/json': + schema: + type: object + properties: + quiz_id: + description: id of quiz for what question is creating + type: integer + title: + description: title of question. max length 512 + type: string + description: + description: description of question. html/text + type: string + type: + description: type of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating + type: string + required: + description: set true if user MUST answer this question + type: boolean + page: + description: page of question + type: integer + content: + description: json serialized of question content settings + type: string + QuestionGetListReq: + content: + 'application/json': + schema: + type: object + properties: + limit: + description: max items on page + type: integer + offset: + description: page number + type: integer + from: + description: start time of time period. timestamp in seconds + type: integer + to: + description: end time of time period. timestamp in seconds + type: integer + search: + description: string for fulltext search in titles of questions + type: string + type: + description: allow only - text, select, file, variant, images, varimg, emoji, date, number, page, rating or empty string + type: string + deleted: + description: get deleted quizes + type: boolean + required: + description: get only require questions + type: boolean + quiz_id: + description: relation to quiz + type: integer + GetQuizReq: + content: + 'application/json': + schema: + type: object + properties: + quiz_id: + type: string + description: secret string id for customer activity + limit: + type: integer + description: page size + page: + type: integer + description: page of questions + need_config: + type: boolean + description: make true if you need all quiz settings with questions page + UpdateQuestionReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of question for update + required: true + title: + type: string + description: new title of question + desc: + type: string + description: new fulltext plain/html description + type: + type: string + description: new type of question + content: + type: string + description: new content of question + required: + type: boolean + description: set true if this question must be answered + page: + type: integer + description: page of question + UpdateQuizReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of question for update + required: true + fp: + type: boolean + description: set true for storing fingerprints + required: true + rep: + type: boolean + description: set true for allow to repeat quiz after passing + required: true + note_prevented: + type: boolean + description: set true for store unfinished passing + required: true + mailing: + type: boolean + description: set true if we should send passing result on every passing + required: true + uniq: + type: boolean + description: set true if we allow only one user quiz passing + required: true + name: + type: string + description: new name of the quiz + desc: + type: string + description: new descriptions of the quiz + conf: + type: string + description: new config of the quiz + status: + type: string + description: new status. only draft,template,stop,start allowed + limit: + type: integer + description: max amount of quiz passing + required: true + due_to: + type: integer + description: max time of quiz passing + required: true + time_of_passing: + type: integer + description: max time to pass quiz + required: true + pausable: + type: boolean + description: allow to pause quiz to user + required: true + question_cnt: + description: count of questions + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + CopyQuestionReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of copying question + required: true + quiz_id: + type: integer + description: quiz id for copy question to another quiz + CopyQuizReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of copied quiz + required: true + GetQuizHistoryReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of quiz for history + required: true + l: + type: integer + description: naumber of quiz due page + p: + type: integer + description: number of page + DeactivateReq: + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + description: id of deactivating struct + required: true + GetStatisticsReq: + content: + 'application/json': + schema: + type: object + properties: + to: + description: таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет. верхняя граница времени + type: integer + from: + description: таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет. нижняя граница времени + type: integer + responses: + GetQuizResultsResp: + description: список результатов, уместившийся на запрошенной странице + content: + 'application/json': + schema: + type: object + properties: + total_count: + description: общее количество элементов удволетворяющее фильтру. нужно для формирования логики пагинации + type: integer + results: + description: список результатов + type: array + items: + type: object + properties: + content: + description: содержимое ответа + type: string + id: + description: айдишник ответа + type: integer + new: + description: статус, был ли просмотрен ответ + type: boolean + created_at: + description: время создания этого результата + type: string + GetDevicesStatsResp: + description: ответ статистики по устройствам + content: + 'application/json': + schema: + type: object + properties: + device: + description: процент устройств среди респондентов + type: object + additionalProperties: + type: number + os: + description: процент операционок среди респондентов + type: object + additionalProperties: + type: number + browser: + description: процент браузеров среди респондентов + type: object + additionalProperties: + type: number + GetGeneralStatsResp: + description: ответ статистики по графикам + content: + 'application/json': + schema: + type: object + properties: + open: + description: ассоциативный массив где ключ - время, а значение - количество открытий + type: object + additionalProperties: + type: number + result: + description: ассоциативный массив, где ключ - время, а значение - количество респондентов, оставивших заявку + type: object + additionalProperties: + type: number + avtime: + description: ассоциативный массив, где ключ - время, а значение - среднее время между первым ответом и последним + type: object + additionalProperties: + type: number + conversation: + description: ассоциативный массив, где ключ - время, а значение - отношение результатов к открытиям + type: object + additionalProperties: + type: number + GetQuestionStatsResp: + description: ответ статистики по вопросам и результатам + content: + 'application/json': + schema: + type: object + properties: + funnel: + description: массив процентов прохождения по опросу + type: array + items: + type: number + results: + description: ассоциативный массив, где ключ - заголовок результата, а значение - количество респондентов, оставивших заявку + type: object + additionalProperties: + type: number + questions: + description: ассоциативный массив, где ключ - id ответа, а значение - ассоциативный массив, где ключ - ответ на вопрос, а значение - процент респондентов, давших такой ответ + type: object + additionalProperties: + type: object + additionalProperties: + type: number + GetStatisticResp: + description: ответ статистики по общему количеству создания квизов, регистрации пользователей, и полученых ответов с result=true, а указанный промежуток времени + content: + 'application/json': + schema: + type: object + properties: + registrations: + description: количество регистраций + type: number + quizes: + description: количество созданных не удаленных опросов + type: number + results: + description: количество ответов с result=true + type: number + QuizModel: + description: object of created quiz + content: + 'application/json': + schema: + type: object + properties: + id: + description: Id of created quiz + type: integer + qid: + description: string id for customers + type: string + deleted: + description: true if quiz deleted + type: boolean + archived: + description: true if quiz archived + type: boolean + fingerprinting: + description: set true for save deviceId + type: boolean + repeatable: + description: set true for allow user to repeat quiz + type: boolean + note_prevented: + description: set true for save statistic of incomplete quiz passing + type: boolean + mail_notifications: + description: set true for mail notification for each quiz passing + type: boolean + unique_answers: + description: set true for save statistics only for unique quiz passing + type: boolean + name: + description: name of quiz. max 280 length + type: string + description: + description: description of quiz + type: string + config: + description: config of quiz. serialized json for rules of quiz flow + type: string + status: + description: status of quiz. allow only '', 'draft', 'template', 'stop', 'start' + type: string + limit: + description: limit is count of max quiz passing + type: integer + due_to: + description: last time when quiz is valid. timestamp in seconds + type: integer + time_of_passing: + description: seconds to pass quiz + type: integer + pausable: + description: true if it is allowed for pause quiz + type: boolean + version: + description: version of quiz + type: integer + version_comment: + description: version comment to version of quiz + type: string + parent_ids: + description: array of previous versions of quiz + type: array + items: + type: integer + created_at: + description: time of creating + type: string + updated_at: + description: time of last updating + type: string + question_cnt: + description: count of questions + type: integer + passed_count: + description: count passings + type: integer + average_time: + description: average time of passing + type: integer + super: + description: set true if squiz realize group functionality + type: boolean + group_id: + description: group of new quiz + type: integer + QuizListGetResp: + description: list of quizes with all filtered quizes count + content: + 'application/json': + schema: + type: object + properties: + count: + description: count of all filtered items + type: integer + items: + description: count of all filtered items + type: array + items: + $ref: '#/components/responses/QuizModel' + QuestionModel: + description: object of created question + content: + 'application/json': + schema: + type: object + properties: + id: + description: Id of created question + type: integer + quiz_id: + description: relation to quiz + type: integer + title: + description: title of question. max 512 length + type: string + description: + description: description of question + type: string + type: + description: status of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating + type: string + required: + description: user must pass this question + type: boolean + deleted: + description: true if question is deleted + type: boolean + page: + description: page if question + type: integer + content: + description: serialized json of created question + type: string + version: + description: version of quiz + type: integer + parent_ids: + description: array of previous versions of quiz + type: array + items: + type: integer + created_at: + description: time of creating + type: string + updated_at: + description: time of last updating + type: string + QuestionListGetResp: + description: list of quizes with all filtered quizes count + content: + 'application/json': + schema: + type: object + properties: + count: + description: count of all filtered items + type: integer + items: + description: count of all filtered items + type: array + items: + $ref: '#/components/responses/QuizModel' + QuizGetResp: + description: short response for customer + content: + 'application/json': + schema: + type: object + properties: + cnt: + type: integer + description: count of all questions in quiz + settings: + type: object + properties: + fp: + type: boolean + description: store fingerprint if true + rep: + type: boolean + description: allow repeat after end of quiz passing + name: + type: string + description: title of quiz + cfg: + type: string + description: serialized json of quiz graph + lim: + type: integer + description: max amount of passing + due: + type: integer + description: last time of new ansvers are valid + delay: + type: integer + description: time that given to customer for pass quiz + pausable: + type: boolean + description: allow user to pause quiz passing + items: + type: array + description: array of xhort items of quiestions for quiz + items: + type: object + properties: + id: + type: integer + description: identificator of question + title: + type: string + description: title of question + desc: + type: string + description: description of question html plaintext + typ: + type: string + description: type of question such as text, select, file, variant, images, varimg, emoji, date, number, page, rating + req: + type: boolean + description: mark question as required + p: + type: integer + description: page of quiz + c: + type: string + description: config of question. such as variants for select + UpdateResp: + description: id of new version of question + content: + 'application/json': + schema: + type: object + properties: + updated: + type: integer + DeactivateResp: + description: id of deleted structure + content: + 'application/json': + schema: + type: object + properties: + deactivated: + type: integer + schemas: + Statistic: + type: object + properties: + Count: + type: integer + format: int64 + description: Количество прошедших сессий + QuestionID: + type: integer + format: int64 + description: Идентификатор вопроса + + PipeLineStatsResp: + type: object + properties: + PipelineStatistic: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Statistic' + description: Статистика по воронкам + + ContactFormStatistic: + type: object + additionalProperties: + type: integer + format: int64 + description: Количество ответов на вопрос формы контактов + description: Статистика форм контакта + Answer: + type: object + properties: + Id: + type: integer + description: id ответа + Content: + type: string + description: контент ответа + QuestionId: + type: integer + description: id вопроса к которому ответ + QuizId: + type: integer + description: id опроса к которому ответ + Fingerprint: + type: string + description: fingerprint + Session: + type: string + description: сессия + Result: + type: boolean + description: true or false? + CreatedAt: + type: string + format: date-time + description: таймшап когда ответ создан + New: + type: boolean + description: новый ответ? + Deleted: + type: boolean + description: удален? + LeadTarget: + type: object + properties: + ID: + type: integer + format: int64 + AccountID: + type: string + Type: + type: string + QuizID: + type: integer + format: int32 + Target: + type: string + InviteLink: + type: string + Deleted: + type: boolean + CreatedAt: + type: string + TgAccountStatus: + type: string + enum: + - active + - inactive + - ban + TgAccount: + type: object + properties: + ID: + type: integer + format: int64 + ApiID: + type: integer + format: int32 + ApiHash: + type: string + PhoneNumber: + type: string + Password: + type: string + Status: + $ref: '#/components/schemas/TgAccountStatus' + Deleted: + type: boolean + CreatedAt: + type: string + format: date-time + AuthTgUserReq: + type: object + required: + - ApiID + - ApiHash + - PhoneNumber + - Password + properties: + ApiID: + type: integer + format: int32 + ApiHash: + type: string + PhoneNumber: + type: string + Password: + type: string +paths: + /liveness: + get: + description: Check that service is running + responses: + '200': + description: healthcheck. if it non 200, that service is not running + /readiness: + get: + description: Check that service is ready for handle requests + responses: + '200': + description: healthcheck. if it non 200, that service is not running + /quiz/create: + post: + description: Create quiz + requestBody: + $ref: '#/components/requestBodies/QuizCreateReq' + responses: + '201': + $ref: '#/components/responses/QuizModel' + '422': + description: name field should have less then 280 chars + content: + 'application/json': + schema: + type: string + default: name field should have less then 280 chars + '406': + description: not acceptable input data + content: + 'application/json': + schema: + type: string + default: status on creating must be only draft,template,stop,start or due to time must be lesser then now + '409': + description: not logical entry + content: + 'application/json': + schema: + type: string + default: you can pause quiz only if it has deadline for passing + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/getList: + post: + description: method for obtain page of quiz list for paginated list + requestBody: + $ref: '#/components/requestBodies/QuizGetListReq' + responses: + '200': + $ref: '#/components/responses/QuizListGetResp' + '406': + description: inappropriate status, allowed only '', 'stop','start','draft', 'template','timeout','offlimit' + content: + 'application/json': + schema: + type: string + default: inappropriate status + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/create: + post: + description: method for creating question for quiz + requestBody: + $ref: '#/components/requestBodies/QuestionCreateReq' + responses: + '200': + $ref: '#/components/responses/QuestionModel' + '422': + description: not valid data + content: + 'application/json': + schema: + type: string + default: title field should have less then 512 chars + '406': + description: not acceptable data + content: + 'application/json': + schema: + type: string + default: type must be only test,button,file,checkbox,select, none + '424': + description: failed relation to quiz + content: + 'application/json': + schema: + type: string + default: failed relation to quiz + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/getList: + post: + description: method for obtain page of question list for paginated list + requestBody: + $ref: '#/components/requestBodies/QuestionGetListReq' + responses: + '200': + $ref: '#/components/responses/QuestionListGetResp' + '406': + description: inappropriate type, allowed only '','test','none','file', 'button','select','checkbox' + content: + 'application/json': + schema: + type: string + default: inappropriate status + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/get: + post: + description: method for obtaining settings of quiz and question page + requestBody: + $ref: '#/components/requestBodies/GetQuizReq' + responses: + '200': + $ref: '#/components/responses/QuizGetResp' + '206': + $ref: '#/components/responses/QuizGetResp' + '400': + description: you cat get quiz only if you know it's uuid + content: + 'application/json': + schema: + type: string + default: no quiz id provided + '411': + description: limit should not be 0 when no aettings is need + content: + 'application/json': + schema: + type: string + default: no data requested + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '423': + description: you can not get data for inactive quiz + content: + 'application/json': + schema: + type: string + default: quiz is inactive + '410': + description: you can not get timeouted quiz + content: + 'application/json': + schema: + type: string + default: quiz timeouted + /question/edit: + patch: + description: method for update question. delete old question and create new with increment version + requestBody: + $ref: '#/components/requestBodies/UpdateQuestionReq' + responses: + '200': + $ref: '#/components/responses/UpdateResp' + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: need id of question for update + '422': + description: too large new title + content: + 'application/json': + schema: + type: string + default: title field should have less then 512 chars + '406': + description: inappropriate status sent + content: + 'application/json': + schema: + type: string + default: type must be only test,button,file,checkbox,select, none or empty string + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/edit: + patch: + description: method for update quiz + requestBody: + $ref: '#/components/requestBodies/UpdateQuizReq' + responses: + '200': + $ref: '#/components/responses/UpdateResp' + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: need id of quiz for update + '422': + description: too large new title + content: + 'application/json': + schema: + type: string + default: name field should have less then 280 chars + '406': + description: inappropriate status sent + content: + 'application/json': + schema: + type: string + default: status on creating must be only draft,template,stop,start or due to time must be lesser then now + '409': + description: impossible state creating + content: + 'application/json': + schema: + type: string + default: you can pause quiz only if it has deadline for passing + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/copy: + post: + description: method for copy question to another quiz or to duplicate quiz + requestBody: + $ref: '#/components/requestBodies/CopyQuestionReq' + responses: + '201': + $ref: '#/components/responses/UpdateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/copy: + post: + description: method for copy question to another quiz or to duplicate quiz + requestBody: + $ref: '#/components/requestBodies/CopyQuizReq' + responses: + '201': + $ref: '#/components/responses/UpdateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/history: + post: + description: method for obtain page of quiz history for paginated list + requestBody: + $ref: '#/components/requestBodies/GetQuizHistoryReq' + responses: + '200': + description: page of history quiz changes + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/responses/QuizModel' + '424': + description: no id provided + content: + 'application/json': + schema: + type: string + default: no id provided + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /question/history: + post: + description: method for obtain page of question history for paginated list + requestBody: + $ref: '#/components/requestBodies/GetQuizHistoryReq' + responses: + '200': + description: page of history question changes + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/responses/QuestionModel' + '424': + description: no id provided + content: + 'application/json': + schema: + type: string + default: no id provided + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /quiz/delete: + delete: + description: method for mark quiz as deleted + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /quiz/archive: + patch: + description: method for mark quiz as archived + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no quiz id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /question/delete: + delete: + description: method for mark question as deleted + requestBody: + $ref: '#/components/requestBodies/DeactivateReq' + responses: + '200': + $ref: '#/components/responses/DeactivateResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + '424': + description: return when no question id was send + content: + 'application/json': + schema: + type: string + default: no id provided + /results/getResults/{quizId}: + post: + description: получение списка результатов респондентов проходивших опрос + requestBody: + $ref: '#/components/requestBodies/GetQuizResultsReq' + responses: + '200': + $ref: '#/components/responses/GetQuizResultsResp' + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /results/delete/{resultId}: + delete: + description: мягкое удаление результата. не удаляет, но помечает удаленным + responses: + '200': + description: если все хорошо,то и отправлять нечего + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /results/seen: + patch: + description: устаревает результаты, т.е. делает их new = false + requestBody: + content: + 'application/json': + schema: + type: object + properties: + answers: + description: список айдишников результатов, которые надо устареть + type: array + items: + type: integer + responses: + '200': + description: если все хорошо,то и отправлять нечего + '500': + description: not mentioned error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + + /results/{quizID}/export: + post: + summary: экспорт результатов quiz в csv + description: экспорт результатов quiz в csv + parameters: + - in: path + name: quizID + description: id quiz, из которого нужно экспортировать результаты + required: true + schema: + type: integer + format: uint64 + requestBody: + required: false + content: + application/json: + schema: + type: object + properties: + to: + type: string + format: date + description: Дата окончания диапазона времени для экспорта результатов + from: + type: string + format: date + description: Дата начала временного диапазона для экспорта результатов + new: + type: boolean + description: Экспортировать ли только новые результаты? + responses: + "200": + description: CSV-файл, содержащий результаты теста + content: + text/csv: + schema: + type: string + format: binary + "400": + description: Bad request. Invalid quiz ID or request body. + "500": + description: Internal server error. Failed to export quiz results. + + /result/{resultID}: + get: + summary: возвращает все ответы по resultID (answerID) в рамках сессии resultID + description: возвращает все ответы по resultID (answerID) в рамках сессии resultID + parameters: + - in: path + name: resultID + required: true + description: resultID (answerID), в рамках сессии которого нужно вернуть все ответы + schema: + type: string + responses: + '200': + description: Successful + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Answer' + '400': + description: Invalid result ID provided + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string + + /statistic/{quizID}/questions: + post: + description: метод получения статистики по конкретным вопросам + parameters: + - in: path + name: quizID + required: true + description: quizID, в рамках которого надо вернуть статистику + schema: + type: string + requestBody: + $ref: '#/components/requestBodies/GetStatisticsReq' + responses: + '200': + $ref: '#/components/responses/GetQuestionStatsResp' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string + /statistic/{quizID}/general: + post: + description: метод получения основной статистики + parameters: + - in: path + name: quizID + required: true + description: quizID, в рамках которого надо вернуть статистику + schema: + type: string + requestBody: + $ref: '#/components/requestBodies/GetStatisticsReq' + responses: + '200': + $ref: '#/components/responses/GetGeneralStatsResp' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string + /statistic/{quizID}/devices: + post: + description: метод получения статистики по устройствам + parameters: + - in: path + name: quizID + required: true + description: quizID, в рамках которого надо вернуть статистику + schema: + type: string + requestBody: + $ref: '#/components/requestBodies/GetStatisticsReq' + responses: + '200': + $ref: '#/components/responses/GetDevicesStatsResp' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string + /statistic: + post: + description: метод получения общей статистики по опросам, регистрациям и ответов с result = true за указанный промежуток времени + requestBody: + $ref: '#/components/requestBodies/GetStatisticsReq' + responses: + '200': + $ref: '#/components/responses/GetStatisticResp' + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed to retrieve result answers + content: + application/json: + schema: + type: object + properties: + message: + type: string + /quiz/template: + post: + description: метод для копирования шаблонного квиза на аккаунт пользователя + requestBody: + content: + 'application/json': + schema: + type: object + properties: + Qid: + type: string + description: qid виза который копируется + required: true + responses: + '200': + description: возвращает id нового квиза + content: + application/json: + schema: + type: object + properties: + id: + type: number + '400': + description: Invalid request + content: + application/json: + schema: + type: object + properties: + message: + type: string + '401': + description: Unauthorized + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Failed copy quiz + content: + application/json: + schema: + type: object + properties: + message: + type: string + /account/manualdone: + post: + description: метод для декремента привилегии quizManual у пользователя + requestBody: + content: + 'application/json': + schema: + type: object + properties: + id: + type: string + description: id пользователя у которого отнимается штука привилегии + required: true + responses: + '200': + description: ОК, успешно отнялась 1 штука + '400': + description: Bad request, ошибка в теле запроса + content: + application/json: + schema: + type: object + properties: + message: + type: string + '404': + description: Not Found, у пользователя не нашлось такой привилегии либо она кончилась и воркер до нее еще не добрался + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Internal Srv Error + content: + application/json: + schema: + type: object + properties: + message: + type: string + /account/leadtarget: + post: + description: Метод для добавления целевых мест, куда будут посылаться заявки клиенту. + security: + - Bearer: [ ] + requestBody: + content: + 'application/json': + schema: + type: object + required: + - type + - quizID + - target + - webhook + properties: + type: + type: string + description: Тип цели (mail, telegram, whatsapp,webhook). + enum: + - mail + - telegram + - whatsapp + - webhook + quizID: + type: integer + format: int32 + description: ID квиза, к которому прикреплено это правило (приоритет). Передавать как 0, если правило не прикрепляется к квизу и является общим. + target: + type: string + description: Адресат, куда конкретно слать (для mail - email, для telegram - ID канала, передавать не нужно канал сам создаться, для whatsapp - номер телефона, наверное, для webhook - url куда слать пост запрос). + name: + type: string + description: имя например для тг канала + responses: + '200': + description: ОК, парвило добавлено если тип mail о сразу добавляется если тг то будет добавленно в воркере если ватсап пока тодо +# content: +# application/json: +# schema: +# $ref: '#/components/schemas/LeadTarget' + '400': + description: Bad request, ошибка в теле запроса + content: + application/json: + schema: + type: object + properties: + message: + type: string + '401': + description: Unauthorized, не авторизован + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Internal Srv Error + content: + application/json: + schema: + type: object + properties: + message: + type: string + put: + description: Метод для обновления целевого места, куда будут посылаться заявки клиенту. + security: + - Bearer: [ ] + requestBody: + content: + 'application/json': + schema: + type: object + required: + - id + - target + properties: + id: + type: integer + format: int64 + description: id этой самой цели, primary key. + target: + type: string + description: Адресат, куда конкретно слать (для mail - email, для telegram - ID чата, для whatsapp - номер телефона, наверное). + responses: + '200': + description: ОК, парвило обновлено + content: + application/json: + schema: + $ref: '#/components/schemas/LeadTarget' + '400': + description: Bad request, ошибка в теле запроса + content: + application/json: + schema: + type: object + properties: + message: + type: string + '401': + description: Unauthorized, не авторизован + content: + application/json: + schema: + type: object + properties: + message: + type: string + '404': + description: NotFound, такого не существует + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Internal Srv Error + content: + application/json: + schema: + type: object + properties: + message: + type: string + /account/leadtarget/{id}: + delete: + description: удаление правила по id, primary key + security: + - Bearer: [ ] + responses: + '200': + description: ОК, парвило удалено + '400': + description: Bad request, ошибка в теле запроса + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Internal Srv Error + content: + application/json: + schema: + type: object + properties: + message: + type: string + /account/leadtarget/{quizID}: + get: + description: получение правила по quizID, так же стоит передавать 0 если правило не было привязано к определенному квизу, возвращает массив + security: + - Bearer: [ ] + responses: + '200': + description: ОК, парвила получены + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/LeadTarget' + '400': + description: Bad request, ошибка в теле запроса + content: + application/json: + schema: + type: object + properties: + message: + type: string + '401': + description: Unauthorized, не авторизован + content: + application/json: + schema: + type: object + properties: + message: + type: string + '404': + description: NotFound, такого не существует + content: + application/json: + schema: + type: object + properties: + message: + type: string + '500': + description: Internal Srv Error + content: + application/json: + schema: + type: object + properties: + message: + type: string + /statistics/:quizID/pipelines: + get: + description: получение статистики по векторам прохождения респондентами опроса с ветвлением и без, на выход отдается мапа с ключем последний вопрос и массивом точек "точек прохождения пользователем вопросов" грубо говоря массив с векторами как двигался респондент по возможным путям, в этом массиве question id и count прошедших сессий через него + parameters: + - name: quizID + in: path + required: true + description: id опроса + schema: + type: integer + requestBody: + $ref: '#/components/requestBodies/GetStatisticsReq' + responses: + '200': + description: Успешное получение статистики + content: + application/json: + schema: + $ref: '#/components/schemas/PipeLineStatsResp' + '400': + description: Bad Request + '500': + description: Internal Server Error + /telegram/pool: + get: + description: возвращает все неудаленные аккаунты тг, активные, не активные и баны, тело пустое + responses: + '200': + description: успех + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TgAccount' + /telegram/create: + post: + description: метод для автторизации сервера в тг аккаунте + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AuthTgUserReq' + responses: + '200': + description: возвращает подпись, которая является идентификатором текущей сессии авторизации нужно для метода отправки кода + content: + application/json: + schema: + type: object + properties: + signature: + type: string + example: b7gh83j2k4l0 + '400': + description: неверные данные запроса + '409': + description: аккаунт уже существует и активен + '500': + description: внутренняя ошибка сервера + /telegram/{id}: + delete: + description: метод мягкого удаления аккаунта по id primary key + parameters: + - in: path + name: id + required: true + description: id primary key + schema: + type: integer + format: int64 + responses: + '200': + description: успех + '400': + description: неверные данные запроса + '500': + description: внутренняя ошибка сервера + + /telegram/setCode: + post: + description: метод для отправки кода авторизации, который пришел от телеграмма + requestBody: + content: + application/json: + schema: + type: object + required: + - code + - signature + properties: + code: + type: string + signature: + type: string + responses: + '200': + description: возвращает id primary авторизованного аккаунта + content: + application/json: + schema: + type: object + properties: + id: + type: integer + format: int64 + '204': + description: state канал закрылся до того как перешел в состояние логина или отказа от логина, возможно стоит другой статус указывать или как то побороть эту беду + '400': + description: неверные данные запроса + '403': + description: что то пошло не так связано с тг + '500': + description: внутренняя ошибка сервера + + /quiz/{quizID}/auditory: + post: + description: Create quiz auditory + parameters: + - name: quizID + in: path + required: true + schema: + type: string + description: ID of the quiz + responses: + '200': + description: Quiz auditory created successfully + content: + 'application/json': + schema: + type: object + properties: + id: + type: integer + format: int64 + description: ID of created auditory + '400': + description: Invalid quiz ID + content: + 'application/json': + schema: + type: string + default: invalid quiz id + '402': + description: Payment required need gigachat privilege + content: + 'application/json': + schema: + type: string + default: payment required + '500': + description: Internal server error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + get: + description: Get quiz auditory + parameters: + - name: quizID + in: path + required: true + schema: + type: string + description: ID of the quiz + responses: + '200': + description: Quiz auditory retrieved successfully + content: + 'application/json': + schema: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + description: ID of auditory + quiz_id: + type: integer + format: int64 + description: ID of the quiz + sex: + type: integer + description: Gender of the person 0 - female, 1 - male, 2 - not sex + age: + type: string + description: Age of the person + deleted: + type: boolean + description: Whether the auditory record is deleted + created_at: + type: integer + format: int64 + description: Creation timestamp + '400': + description: Invalid quiz ID + content: + 'application/json': + schema: + type: string + default: invalid quiz id + '404': + description: Quiz auditory not found + content: + 'application/json': + schema: + type: string + default: quiz auditory not found + '500': + description: Internal server error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + delete: + description: Delete quiz auditory + parameters: + - name: quizID + in: path + required: true + schema: + type: string + description: ID of the quiz + responses: + '200': + description: Quiz auditory deleted successfully + content: + 'application/json': + schema: + type: object + properties: + deleted: + type: boolean + description: True if auditory was deleted + '400': + description: Invalid quiz ID + content: + 'application/json': + schema: + type: string + default: invalid quiz id + '404': + description: Quiz auditory not found + content: + 'application/json': + schema: + type: string + default: quiz auditory not found + '500': + description: Internal server error + content: + 'application/json': + schema: + type: string + default: if you get any content string send it to developer + /utm/{quizID}: + get: + summary: Получить все UTM по quizID + parameters: + - name: quizID + in: path + required: true + description: Идентификатор опроса + schema: + type: integer + format: int64 + responses: + '200': + description: Список UTM + content: + application/json: + schema: + type: array + items: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + quiz_id: + type: integer + format: int64 + example: 123 + utm: + type: string + example: utm_campaign=test + deleted: + type: boolean + example: false + created_at: + type: string + format: date-time + example: "2024-06-23T12:00:00Z" + '400': + description: Bad Request + '500': + description: Internal server error + + /utm: + post: + summary: Создать новую UTM запись + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - quiz_id + - utm + properties: + quiz_id: + type: integer + format: int64 + example: 123 + utm: + type: string + example: utm_campaign=test + responses: + '200': + description: Созданная UTM запись + content: + application/json: + schema: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + quiz_id: + type: integer + format: int64 + example: 123 + utm: + type: string + example: utm_campaign=test + deleted: + type: boolean + example: false + created_at: + type: string + format: date-time + example: "2024-06-23T12:00:00Z" + '400': + description: Bad Request + '500': + description: Internal server error + + /utm/{utmID}: + delete: + summary: Мягкое удаление UTM по ID + parameters: + - name: utmID + in: path + required: true + description: Идентификатор UTM + schema: + type: integer + format: int64 + responses: + '200': + description: Success delete + '400': + description: Bad Request + '500': + description: Internal server error \ No newline at end of file diff --git a/src/pages/IntegrationsPage/IntegrationsModal/Postback/index.tsx b/src/pages/IntegrationsPage/IntegrationsModal/Postback/index.tsx index 3b7af294..4a701275 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/Postback/index.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/Postback/index.tsx @@ -1,7 +1,10 @@ -import { FC } from "react"; -import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box } from "@mui/material"; +import { FC, useState } from "react"; +import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Button, Link } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import { Quiz } from "@/model/quiz/quiz"; +import CustomTextField from "@/ui_kit/CustomTextField"; +import OrangeYoutube from "@/assets/icons/OrangeYoutube"; +import { createLeadTarget } from "@/api/leadtarget"; type PostbackModalProps = { isModalOpen: boolean; @@ -10,15 +13,17 @@ type PostbackModalProps = { quiz: Quiz; }; -export const PostbackModal: FC = ({ - isModalOpen, - handleCloseModal, - companyName, - quiz +export const PostbackModal: FC = ({ + isModalOpen, + handleCloseModal, + companyName, + quiz }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(600)); const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const [token, setToken] = useState(""); + const [domain, setDomain] = useState(""); return ( = ({ PaperProps={{ sx: { maxWidth: isTablet ? "100%" : "919px", - height: "658px", + height: "314px", borderRadius: "12px", }, }} > - - + - + + + + + + + Смотреть инструкцию + + + + + + Токен авторизации + + setToken(e.target.value)} + maxLength={150} + /> + + Домен + + setDomain(e.target.value)} + maxLength={150} + /> + + + diff --git a/src/pages/IntegrationsPage/IntegrationsModal/Zapier/index.tsx b/src/pages/IntegrationsPage/IntegrationsModal/Zapier/index.tsx index 39483a96..627f202a 100644 --- a/src/pages/IntegrationsPage/IntegrationsModal/Zapier/index.tsx +++ b/src/pages/IntegrationsPage/IntegrationsModal/Zapier/index.tsx @@ -1,7 +1,10 @@ -import { FC } from "react"; -import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box } from "@mui/material"; +import { FC, useState } from "react"; +import { Dialog, IconButton, Typography, useMediaQuery, useTheme, Box, Link, Button } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import { Quiz } from "@/model/quiz/quiz"; +import OrangeYoutube from "@/assets/icons/OrangeYoutube"; +import CustomTextField from "@/ui_kit/CustomTextField"; +import { createLeadTarget } from "@/api/leadtarget"; type ZapierModalProps = { isModalOpen: boolean; @@ -10,15 +13,16 @@ type ZapierModalProps = { quiz: Quiz; }; -export const ZapierModal: FC = ({ - isModalOpen, - handleCloseModal, - companyName, - quiz +export const ZapierModal: FC = ({ + isModalOpen, + handleCloseModal, + companyName, + quiz }) => { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down(600)); const isTablet = useMediaQuery(theme.breakpoints.down(1000)); + const [webhookUrl, setWebhookUrl] = useState(""); return ( = ({ PaperProps={{ sx: { maxWidth: isTablet ? "100%" : "919px", - height: "658px", + height: "195px", borderRadius: "12px", }, }} @@ -52,7 +56,7 @@ export const ZapierModal: FC = ({ color: theme.palette.grey2.main, }} > - Интеграция с {companyName ? companyName : "Zapier"} + Автоматизация с {companyName ? companyName : "Zapier"} @@ -65,9 +69,79 @@ export const ZapierModal: FC = ({ overflow: "auto", }} > - - Интеграция с Zapier находится в разработке. - + + Смотреть инструкцию + + + + + URL webhook + + + setWebhookUrl(e.target.value)} + maxLength={150} + /> + + + + diff --git a/src/pages/IntegrationsPage/IntegrationsPage.tsx b/src/pages/IntegrationsPage/IntegrationsPage.tsx index ce2af29a..b9eea91d 100644 --- a/src/pages/IntegrationsPage/IntegrationsPage.tsx +++ b/src/pages/IntegrationsPage/IntegrationsPage.tsx @@ -5,6 +5,7 @@ import { useCurrentQuiz } from "@root/quizes/hooks"; import { useQuizStore } from "@root/quizes/store"; import { useNavigate } from "react-router-dom"; import { PartnersBoard } from "./PartnersBoard/PartnersBoard"; +import { getLeadTargetsByQuiz } from "@/api/leadtarget"; import { QuizMetricType } from "@model/quizSettings"; interface IntegrationsPageProps { @@ -29,10 +30,25 @@ export const IntegrationsPage = ({ const [isAmoCrmModalOpen, setIsAmoCrmModalOpen] = useState(false); const [isZapierModalOpen, setIsZapierModalOpen] = useState(false); const [isPostbackModalOpen, setIsPostbackModalOpen] = useState(false); + const [leadTargetsLoaded, setLeadTargetsLoaded] = useState(false); + const [leadTargets, setLeadTargets] = useState(null); + useEffect(() => { if (editQuizId === null) navigate("/list"); }, [navigate, editQuizId]); + + useEffect(() => { + // Загрузка связанных с квизом данных интеграций при входе на страницу + const load = async () => { + if (!leadTargetsLoaded && quiz?.id) { + const [items] = await getLeadTargetsByQuiz(quiz.backendId); + setLeadTargets(items ?? []); + setLeadTargetsLoaded(true); + } + }; + load(); + }, [leadTargetsLoaded, quiz?.id]); const heightBar = heightSidebar + 51 + 88 + 36 + 25; if (quiz === undefined) diff --git a/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx b/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx index 5217c134..00b6f4e4 100644 --- a/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx +++ b/src/pages/IntegrationsPage/PartnersBoard/PartnersBoard.tsx @@ -132,7 +132,7 @@ export const PartnersBoard: FC = ({ /> - {/* + Автоматизация @@ -144,7 +144,7 @@ export const PartnersBoard: FC = ({ setIsModalOpen={setIsPostbackModalOpen} setCompanyName={setCompanyName} /> - */} + {companyName && (