api файл и отображение списка
This commit is contained in:
parent
6273e62e66
commit
c2d79c04cc
577
api-docs.html
Normal file
577
api-docs.html
Normal file
@ -0,0 +1,577 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>QUIZ Service API Documentation</title>
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #7E2AEA;
|
||||
--text-color: #333;
|
||||
--bg-color: #fff;
|
||||
--border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
padding: 2rem 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.endpoint {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.method {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.get { background: #61affe; }
|
||||
.post { background: #49cc90; }
|
||||
.put { background: #fca130; }
|
||||
.delete { background: #f93e3e; }
|
||||
.patch { background: #50e3c2; }
|
||||
|
||||
.schema {
|
||||
background: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.nav a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.nav a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f1f1f1;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.response {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.components {
|
||||
margin: 2rem 0;
|
||||
padding: 1rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.security {
|
||||
background: #fff3cd;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.parameter {
|
||||
margin: 0.5rem 0;
|
||||
padding: 0.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.parameter.required {
|
||||
border-left: 4px solid #dc3545;
|
||||
}
|
||||
|
||||
.enum-values {
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1>QUIZ Service API Documentation</h1>
|
||||
<p>Version 1.0.0</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<nav class="nav">
|
||||
<div class="container">
|
||||
<a href="#components">Components</a>
|
||||
<a href="#quiz">Quiz Endpoints</a>
|
||||
<a href="#question">Question Endpoints</a>
|
||||
<a href="#results">Results Endpoints</a>
|
||||
<a href="#statistics">Statistics Endpoints</a>
|
||||
<a href="#account">Account Endpoints</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container">
|
||||
<section id="components">
|
||||
<h2>Components</h2>
|
||||
|
||||
<div class="components">
|
||||
<h3>Quiz Model</h3>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"id": integer, // Id of created quiz
|
||||
"qid": string, // string id for customers
|
||||
"deleted": boolean, // true if quiz deleted
|
||||
"archived": boolean, // true if quiz archived
|
||||
"fingerprinting": boolean, // set true for save deviceId
|
||||
"repeatable": boolean, // set true for allow user to repeat quiz
|
||||
"note_prevented": boolean, // set true for save statistic of incomplete quiz passing
|
||||
"mail_notifications": boolean, // set true for mail notification for each quiz passing
|
||||
"unique_answers": boolean, // set true for save statistics only for unique quiz passing
|
||||
"name": string, // name of quiz. max 280 length
|
||||
"description": string, // description of quiz
|
||||
"config": string, // config of quiz. serialized json for rules of quiz flow
|
||||
"status": string, // status of quiz. allow only '', 'draft', 'template', 'stop', 'start'
|
||||
"limit": integer, // limit is count of max quiz passing
|
||||
"due_to": integer, // last time when quiz is valid. timestamp in seconds
|
||||
"time_of_passing": integer, // seconds to pass quiz
|
||||
"pausable": boolean, // true if it is allowed for pause quiz
|
||||
"version": integer, // version of quiz
|
||||
"version_comment": string, // version comment to version of quiz
|
||||
"parent_ids": integer[], // array of previous versions of quiz
|
||||
"created_at": string, // time of creating
|
||||
"updated_at": string, // time of last updating
|
||||
"question_cnt": integer, // count of questions
|
||||
"passed_count": integer, // count passings
|
||||
"average_time": integer, // average time of passing
|
||||
"super": boolean, // set true if squiz realize group functionality
|
||||
"group_id": integer // group of new quiz
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="components">
|
||||
<h3>Question Model</h3>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"id": integer, // Id of created question
|
||||
"quiz_id": integer, // relation to quiz
|
||||
"title": string, // title of question. max 512 length
|
||||
"description": string, // description of question
|
||||
"type": string, // status of question. allow only text, select, file, variant, images, varimg, emoji, date, number, page, rating
|
||||
"required": boolean, // user must pass this question
|
||||
"deleted": boolean, // true if question is deleted
|
||||
"page": integer, // page if question
|
||||
"content": string, // serialized json of created question
|
||||
"version": integer, // version of quiz
|
||||
"parent_ids": integer[], // array of previous versions of quiz
|
||||
"created_at": string, // time of creating
|
||||
"updated_at": string // time of last updating
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="components">
|
||||
<h3>Answer Model</h3>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"Id": integer, // id ответа
|
||||
"Content": string, // контент ответа
|
||||
"QuestionId": integer, // id вопроса к которому ответ
|
||||
"QuizId": integer, // id опроса к которому ответ
|
||||
"Fingerprint": string, // fingerprint
|
||||
"Session": string, // сессия
|
||||
"Result": boolean, // true or false?
|
||||
"CreatedAt": string, // таймшап когда ответ создан
|
||||
"New": boolean, // новый ответ?
|
||||
"Deleted": boolean // удален?
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="components">
|
||||
<h3>LeadTarget Model</h3>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"ID": integer, // primary key
|
||||
"AccountID": string, // account identifier
|
||||
"Type": string, // type of target (mail, telegram, whatsapp)
|
||||
"QuizID": integer, // ID of the quiz
|
||||
"Target": string, // target address
|
||||
"InviteLink": string, // invitation link
|
||||
"Deleted": boolean, // is deleted
|
||||
"CreatedAt": string // creation timestamp
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="components">
|
||||
<h3>TgAccount Model</h3>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"ID": integer, // primary key
|
||||
"ApiID": integer, // Telegram API ID
|
||||
"ApiHash": string, // Telegram API Hash
|
||||
"PhoneNumber": string, // phone number
|
||||
"Password": string, // account password
|
||||
"Status": string, // account status (active, inactive, ban)
|
||||
"Deleted": boolean, // is deleted
|
||||
"CreatedAt": string // creation timestamp
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="quiz">
|
||||
<h2>Quiz Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Create Quiz</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/quiz/create</code>
|
||||
<p>Create a new quiz with specified parameters.</p>
|
||||
|
||||
<div class="security">
|
||||
<h4>Security</h4>
|
||||
<p>This endpoint requires authentication.</p>
|
||||
</div>
|
||||
|
||||
<h4>Request Body:</h4>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"fingerprinting": boolean, // set true for save deviceId
|
||||
"repeatable": boolean, // set true for allow user to repeat quiz
|
||||
"note_prevented": boolean, // set true for save statistic of incomplete quiz passing
|
||||
"mail_notifications": boolean, // set true for mail notification for each quiz passing
|
||||
"unique_answers": boolean, // set true for save statistics only for unique quiz passing
|
||||
"name": string, // name of quiz. max 280 length
|
||||
"description": string, // description of quiz
|
||||
"config": string, // config of quiz. serialized json for rules of quiz flow
|
||||
"status": string, // status of quiz. allow only '', 'draft', 'template', 'stop', 'start'
|
||||
"limit": integer, // limit is count of max quiz passing
|
||||
"due_to": integer, // last time when quiz is valid. timestamp in seconds
|
||||
"time_of_passing": integer, // seconds to pass quiz
|
||||
"pausable": boolean, // true if it is allowed for pause quiz
|
||||
"question_cnt": integer, // count of questions
|
||||
"super": boolean, // set true if squiz realize group functionality
|
||||
"group_id": integer // group of new quiz
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>201 Created</h5>
|
||||
<p>Quiz successfully created. Returns the created quiz object.</p>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"id": integer,
|
||||
"qid": string,
|
||||
"name": string,
|
||||
"description": string,
|
||||
// ... other quiz properties
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>422 Unprocessable Entity</h5>
|
||||
<p>Name field should have less than 280 characters.</p>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>406 Not Acceptable</h5>
|
||||
<p>Status on creating must be only draft, template, stop, start or due to time must be lesser than now.</p>
|
||||
<div class="enum-values">
|
||||
Allowed status values: '', 'draft', 'template', 'stop', 'start'
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>409 Conflict</h5>
|
||||
<p>You can pause quiz only if it has deadline for passing.</p>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>500 Internal Server Error</h5>
|
||||
<p>If you get any content string send it to developer.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Get Quiz List</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/quiz/getList</code>
|
||||
<p>Get paginated list of quizzes with filtering options.</p>
|
||||
|
||||
<h4>Request Body:</h4>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"limit": integer,
|
||||
"offset": integer,
|
||||
"from": integer,
|
||||
"to": integer,
|
||||
"search": string,
|
||||
"status": string,
|
||||
"deleted": boolean,
|
||||
"archived": boolean,
|
||||
"super": boolean,
|
||||
"group_id": integer
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>200 OK</h5>
|
||||
<p>Returns list of quizzes with total count.</p>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"count": integer,
|
||||
"items": [
|
||||
{
|
||||
"id": integer,
|
||||
"qid": string,
|
||||
// ... other quiz properties
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>406 Not Acceptable</h5>
|
||||
<p>Inappropriate status, allowed only '', 'stop', 'start', 'draft', 'template', 'timeout', 'offlimit'.</p>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>500 Internal Server Error</h5>
|
||||
<p>If you get any content string send it to developer.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add more quiz endpoints -->
|
||||
</section>
|
||||
|
||||
<section id="question">
|
||||
<h2>Question Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Create Question</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/question/create</code>
|
||||
<p>Create a new question for a quiz.</p>
|
||||
</div>
|
||||
|
||||
<!-- Add more question endpoints -->
|
||||
</section>
|
||||
|
||||
<section id="results">
|
||||
<h2>Results Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Get Quiz Results</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/results/getResults/{quizId}</code>
|
||||
<p>Get list of quiz results with pagination.</p>
|
||||
|
||||
<h4>Path Parameters:</h4>
|
||||
<div class="parameter">
|
||||
<code>quizId</code> - ID of the quiz to get results for
|
||||
</div>
|
||||
|
||||
<h4>Request Body:</h4>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"to": integer, // таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет
|
||||
"from": integer, // таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет
|
||||
"new": boolean, // флаг, по которому вернутся только новые результаты, ещё не просмотренные пользователем
|
||||
"page": integer, // номер страницы для пагинации
|
||||
"limit": integer // размер страницы для пагинации
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>200 OK</h5>
|
||||
<p>Returns paginated list of results.</p>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"total_count": integer, // общее количество элементов удволетворяющее фильтру
|
||||
"results": [
|
||||
{
|
||||
"content": string, // содержимое ответа
|
||||
"id": integer, // айдишник ответа
|
||||
"new": boolean, // статус, был ли просмотрен ответ
|
||||
"created_at": string // время создания этого результата
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Export Results</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/results/{quizID}/export</code>
|
||||
<p>Export quiz results to CSV format.</p>
|
||||
|
||||
<h4>Path Parameters:</h4>
|
||||
<div class="parameter required">
|
||||
<code>quizID</code> - ID of the quiz to export results from
|
||||
</div>
|
||||
|
||||
<h4>Request Body:</h4>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"to": string, // Дата окончания диапазона времени для экспорта результатов
|
||||
"from": string, // Дата начала временного диапазона для экспорта результатов
|
||||
"new": boolean // Экспортировать ли только новые результаты?
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>200 OK</h5>
|
||||
<p>Returns CSV file with quiz results.</p>
|
||||
<div class="schema">
|
||||
<pre><code>Content-Type: text/csv</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="telegram">
|
||||
<h2>Telegram Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Create Telegram Account</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/telegram/create</code>
|
||||
<p>Authorize server in Telegram account.</p>
|
||||
|
||||
<h4>Request Body:</h4>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"ApiID": integer, // Telegram API ID
|
||||
"ApiHash": string, // Telegram API Hash
|
||||
"PhoneNumber": string, // Phone number
|
||||
"Password": string // Account password
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>200 OK</h5>
|
||||
<p>Returns signature for code verification.</p>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"signature": string // Session identifier for code verification
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="response">
|
||||
<h5>409 Conflict</h5>
|
||||
<p>Account already exists and is active.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="audience">
|
||||
<h2>Audience Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Create Quiz Audience</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/quiz/{quizID}/auditory</code>
|
||||
<p>Create audience for a quiz.</p>
|
||||
|
||||
<h4>Path Parameters:</h4>
|
||||
<div class="parameter required">
|
||||
<code>quizID</code> - ID of the quiz
|
||||
</div>
|
||||
|
||||
<h4>Responses:</h4>
|
||||
<div class="response">
|
||||
<h5>200 OK</h5>
|
||||
<p>Returns ID of created audience.</p>
|
||||
<div class="schema">
|
||||
<pre><code>{
|
||||
"id": integer // ID of created auditory
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="statistics">
|
||||
<h2>Statistics Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Get Question Statistics</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/statistic/{quizID}/questions</code>
|
||||
<p>Get statistics for specific questions in a quiz.</p>
|
||||
</div>
|
||||
|
||||
<!-- Add more statistics endpoints -->
|
||||
</section>
|
||||
|
||||
<section id="account">
|
||||
<h2>Account Endpoints</h2>
|
||||
|
||||
<div class="endpoint">
|
||||
<h3>Add Lead Target</h3>
|
||||
<span class="method post">POST</span>
|
||||
<code>/account/leadtarget</code>
|
||||
<p>Add a target destination for lead notifications.</p>
|
||||
</div>
|
||||
|
||||
<!-- Add more account endpoints -->
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="container">
|
||||
<p>© 2024 QUIZ Service API Documentation</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
106
src/api/auditory.ts
Normal file
106
src/api/auditory.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import { makeRequest } from "@frontend/kitui";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`;
|
||||
|
||||
// Types
|
||||
export interface AuditoryItem {
|
||||
id: number;
|
||||
quiz_id: number;
|
||||
sex: boolean;
|
||||
age: string;
|
||||
deleted: boolean;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
export interface AuditoryResponse {
|
||||
data: AuditoryItem[];
|
||||
}
|
||||
|
||||
// Request Types
|
||||
export interface AuditoryGetRequest {
|
||||
quizId: number;
|
||||
}
|
||||
|
||||
export interface AuditoryDeleteRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface AuditoryAddRequest {
|
||||
sex: boolean;
|
||||
age: string;
|
||||
}
|
||||
|
||||
// Parameters
|
||||
export interface AuditoryGetParams {
|
||||
quizId: number;
|
||||
}
|
||||
|
||||
export interface AuditoryDeleteParams {
|
||||
quizId: number;
|
||||
auditoryId: number;
|
||||
}
|
||||
|
||||
export interface AuditoryAddParams {
|
||||
quizId: number;
|
||||
body: AuditoryAddRequest;
|
||||
}
|
||||
|
||||
// API calls
|
||||
export const auditoryGet = async ({ quizId }: AuditoryGetParams): Promise<[AuditoryResponse | null, string?]> => {
|
||||
if (!quizId) {
|
||||
return [null, "Quiz ID is required"];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest<AuditoryGetRequest, AuditoryResponse>({
|
||||
url: `${API_URL}/quiz/${quizId}/auditory`,
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
return [response];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить аудиторию. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const auditoryDelete = async ({ quizId, auditoryId }: AuditoryDeleteParams): Promise<[AuditoryResponse | null, string?]> => {
|
||||
if (!quizId || !auditoryId) {
|
||||
return [null, "Quiz ID and Auditory ID are required"];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest<AuditoryDeleteRequest, AuditoryResponse>({
|
||||
url: `${API_URL}/quiz/${quizId}/auditory`,
|
||||
body: {
|
||||
id: auditoryId
|
||||
},
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
return [response];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось удалить аудиторию. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const auditoryAdd = async ({ quizId, body }: AuditoryAddParams): Promise<[AuditoryResponse | null, string?]> => {
|
||||
if (!quizId) {
|
||||
return [null, "Quiz ID is required"];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest<AuditoryAddRequest, AuditoryResponse>({
|
||||
url: `${API_URL}/quiz/${quizId}/auditory`,
|
||||
body,
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
return [response];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось добавить аудиторию. ${error}`];
|
||||
}
|
||||
};
|
114
src/pages/PersonalizationAI/AuditoryList.tsx
Normal file
114
src/pages/PersonalizationAI/AuditoryList.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
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 { useEffect, useState } from "react";
|
||||
|
||||
const PURPLE = "#7E2AEA";
|
||||
|
||||
export const AuditoryList = () => {
|
||||
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)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{
|
||||
maxWidth: "796px",
|
||||
bgcolor: "#fff",
|
||||
borderRadius: "12px",
|
||||
p: "20px",
|
||||
boxShadow: "0px 4px 32px 0px #7E2AEA14",
|
||||
mt: "24px"
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography sx={{ fontSize: "18px", fontWeight: 500, color: theme.palette.grey3.main }}>
|
||||
Ваши сохраненные ссылки
|
||||
</Typography>
|
||||
<IconButton
|
||||
sx={{ cursor: 'pointer', color: PURPLE, display: 'flex', alignItems: 'center', transition: 'transform 0.2s', transform: linksOpen ? 'rotate(0deg)' : 'rotate(180deg)' }}
|
||||
onClick={() => setLinksOpen((prev) => !prev)}
|
||||
size="large"
|
||||
>
|
||||
<ArrowDownIcon style={{ width: "18px", height: "18px" }} />
|
||||
</IconButton>
|
||||
</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>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Collapse>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
@ -2,24 +2,20 @@ import { Box, Container, Typography, TextField, Button, List, ListItem, IconButt
|
||||
import { InfoPopover } from '@ui_kit/InfoPopover';
|
||||
import CopyIcon from "@/assets/icons/CopyIcon";
|
||||
import GenderAndAgeSelector from "./GenderAndAgeSelector";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
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 { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||
import { AuditoryList } from "./AuditoryList";
|
||||
|
||||
const PURPLE = "#7E2AEA";
|
||||
const GREY_TEXT = "#A0A0A0";
|
||||
const GREY_BORDER = "#E0E0E0";
|
||||
const GREY_ICON = "#B0B0B0";
|
||||
const BLOCK_RADIUS = "16px";
|
||||
const BLOCK_PX = "32px";
|
||||
const BLOCK_PY = "24px";
|
||||
|
||||
export default function PersonalizationAI() {
|
||||
const [gender, setGender] = useState('');
|
||||
const [linksOpen, setLinksOpen] = useState(true);
|
||||
const theme = useTheme();
|
||||
const [gender, setGender] = useState('');
|
||||
|
||||
return (
|
||||
<Container id="PersonalizationAI" maxWidth={false} sx={{ minHeight: "100%", p: "20px" }}>
|
||||
@ -47,9 +43,6 @@ export default function PersonalizationAI() {
|
||||
}}>
|
||||
<GenderAndAgeSelector gender={gender} setGender={setGender} />
|
||||
|
||||
|
||||
|
||||
|
||||
{/* Ссылка */}
|
||||
<Box sx={{ mt: "34px" }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
@ -95,73 +88,8 @@ export default function PersonalizationAI() {
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Второй белый блок */}
|
||||
<Box sx={{
|
||||
maxWidth: "796px",
|
||||
bgcolor: "#fff",
|
||||
borderRadius: "12px",
|
||||
p: "20px",
|
||||
boxShadow: "0px 4px 32px 0px #7E2AEA14",
|
||||
mt: "24px"
|
||||
}}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Typography sx={{ fontSize: "18px", fontWeight: 500, color: theme.palette.grey3.main }}>
|
||||
Ваши сохраненные ссылки
|
||||
</Typography>
|
||||
<IconButton
|
||||
sx={{ cursor: 'pointer', color: PURPLE, display: 'flex', alignItems: 'center', transition: 'transform 0.2s', transform: linksOpen ? 'rotate(0deg)' : 'rotate(180deg)' }}
|
||||
onClick={() => setLinksOpen((prev) => !prev)}
|
||||
size="large"
|
||||
>
|
||||
<ArrowDownIcon style={{ width: "18px", height: "18px" }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Collapse in={linksOpen} timeout="auto" unmountOnExit sx={{ mt: "3px" }}>
|
||||
<List sx={{ gap: '8px', p: 0, m: 0 }}>
|
||||
{[1, 2, 3, 4, 5].map((_, idx) => (
|
||||
<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",
|
||||
<AuditoryList />
|
||||
|
||||
},
|
||||
}}
|
||||
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" }}>
|
||||
|
||||
<CopyIcon
|
||||
color={PURPLE}
|
||||
/>
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Typography sx={{ color: 'black', fontWeight: 400, fontSize: "16px" }}>
|
||||
linkexample.ru
|
||||
</Typography>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Collapse>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user