Темный режим

7.4. Survey - API и интеграция

Интеграция с API

Приложение Survey взаимодействует с API для получения данных опросов и отправки ответов пользователей. Взаимодействие с API реализовано в модуле utils/api.

Структура API модуля

API модуль состоит из нескольких файлов:

  • src/utils/api/index.ts - основной интерфейс для API
  • src/utils/api/surveyApi.ts - функции для работы с API опросов
  • src/utils/api/surveyData.ts - функции для работы с данными опросов
  • src/utils/api/cache.ts - функции для кэширования запросов

Кэширование API-запросов

Для оптимизации работы с API и уменьшения количества запросов реализовано кэширование результатов запросов в localStorage.

Пример кода кэширования

// src/utils/api/cache.ts
/**
 * Получение данных из кэша
 * @param key Ключ кэша
 * @returns Данные из кэша или null, если кэш не найден
 */
export const getCache = (key: string) => {
  try {
    const cacheItem = localStorage.getItem(`cache_${key}`);
    
    if (!cacheItem) {
      return null;
    }
    
    const { data, timestamp } = JSON.parse(cacheItem);
    
    // Проверяем, не устарел ли кэш (24 часа)
    const cacheAge = Date.now() - timestamp;
    if (cacheAge > 24 * 60 * 60 * 1000) {
      localStorage.removeItem(`cache_${key}`);
      return null;
    }
    
    return data;
  } catch (error) {
    console.error('Error getting cache:', error);
    return null;
  }
};

/**
 * Сохранение данных в кэш
 * @param key Ключ кэша
 * @param data Данные для сохранения
 */
export const setCache = (key: string, data: any) => {
  try {
    const cacheItem = {
      data,
      timestamp: Date.now()
    };
    
    localStorage.setItem(`cache_${key}`, JSON.stringify(cacheItem));
  } catch (error) {
    console.error('Error setting cache:', error);
  }
};

/**
 * Очистка кэша
 * @param key Ключ кэша (если не указан, очищается весь кэш)
 */
export const clearCache = (key?: string) => {
  try {
    if (key) {
      localStorage.removeItem(`cache_${key}`);
    } else {
      // Очистка всего кэша
      Object.keys(localStorage).forEach((storageKey) => {
        if (storageKey.startsWith('cache_')) {
          localStorage.removeItem(storageKey);
        }
      });
    }
  } catch (error) {
    console.error('Error clearing cache:', error);
  }
};
                    

Структура API ответов

Типичная структура ответа API для опроса:

Пример структуры ответа API

{
  "questions": [
    {
      "questionId": "q1",
      "question": "Насколько вы удовлетворены своей работой?",
      "type": "stars-5pt",
      "answer": null,
      "hint": "Оцените по шкале от 1 до 5"
    },
    {
      "questionId": "q2",
      "question": "Как бы вы оценили атмосферу в команде?",
      "type": "scale-11pt",
      "answer": null,
      "hint": null
    },
    // ... другие вопросы
  ],
  "question_list": ["q1", "q2", "q3", "q4", "q5"],
  "lang": "ru",
  "companyname": "Example Company"
}
                    

Обработка ошибок API

В проекте реализована обработка ошибок API с помощью try/catch и отображения ошибок пользователю.

Пример обработки ошибок API

/**
 * Общая функция для выполнения API-запросов с обработкой ошибок
 * @param url URL запроса
 * @param options Опции запроса
 * @returns Promise с результатом запроса
 */
export const apiRequest = async (url: string, options?: RequestInit) => {
  try {
    const response = await fetch(url, options);
    
    if (!response.ok) {
      // Проверяем тип ошибки
      if (response.status === 401) {
        throw new Error('Unauthorized');
      } else if (response.status === 404) {
        throw new Error('Resource not found');
      } else {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
    }
    
    return await response.json();
  } catch (error) {
    console.error('API request failed:', error);
    
    // Для некоторых типов ошибок можно выполнить дополнительные действия
    if (error.message === 'Unauthorized') {
      // Например, перенаправить на страницу входа
    }
    
    throw error;
  }
};
                    

Сохранение состояния опроса

Для возможности продолжить опрос позже реализовано сохранение состояния прохождения опроса в localStorage.

Пример сохранения состояния опроса

// src/utils/surveyPersistence.ts
/**
 * Сохранение состояния опроса
 * @param surveyId ID опроса
 * @param userId ID пользователя
 * @param answers Ответы пользователя
 * @param currentQuestionIndex Индекс текущего вопроса
 * @returns true, если сохранение успешно
 */
export const saveSurveyState = (
  surveyId: string,
  userId: string,
  answers: Record<string, any>,
  currentQuestionIndex: number
) => {
  try {
    const stateKey = `surveyState_${surveyId}_${userId}`;
    const state = {
      answers,
      currentQuestionIndex,
      timestamp: new Date().toISOString()
    };
    
    localStorage.setItem(stateKey, JSON.stringify(state));
    return true;
  } catch (error) {
    console.error('Error saving survey state:', error);
    return false;
  }
};

/**
 * Загрузка состояния опроса
 * @param surveyId ID опроса
 * @param userId ID пользователя
 * @returns Сохраненное состояние опроса или null
 */
export const loadSurveyState = (
  surveyId: string,
  userId: string
) => {
  try {
    const stateKey = `surveyState_${surveyId}_${userId}`;
    const stateJson = localStorage.getItem(stateKey);
    
    if (!stateJson) {
      return null;
    }
    
    return JSON.parse(stateJson);
  } catch (error) {
    console.error('Error loading survey state:', error);
    return null;
  }
};

/**
 * Удаление состояния опроса
 * @param surveyId ID опроса
 * @param userId ID пользователя
 * @returns true, если удаление успешно
 */
export const clearSurveyState = (
  surveyId: string,
  userId: string
) => {
  try {
    const stateKey = `surveyState_${surveyId}_${userId}`;
    localStorage.removeItem(stateKey);
    return true;
  } catch (error) {
    console.error('Error clearing survey state:', error);
    return false;
  }
};