Как сделать интерактивную панель для отправки SMS
Сегодня разработаем панель, через которую сможем отправлять SMS сразу нескольким адресатам, получать их ответы и видеть статистику по отправленным сообщениям через календарь и график. Будем использовать Next.js, Shadcn для интерфейса и SMS API от МТС Exolve для отправки сообщений.
Архитектура приложения
Мы создадим приложение, где администратор сможет вводить один или несколько номеров абонентов и текст сообщения и отправлять его одной кнопкой. На сайте реализуем календарь для выбора даты и просмотра всех отправленных SMS за конкретный день, а также график с их количеством.
Подготовка окружения
Разрабатывать будем на фулстек-фреймворке Next.js, который поддерживает серверный и клиентский код на React.js. Задача — создать компонент календаря и графика. Для этого мы обратимся к библиотеке Shadcn с готовыми компонентами, которые настроим под свои нужды. Основу слоя стилей Shadcn составляет Tailwind, и рекомендуется использовать его вместе с TypeScript.
Если вы ещё не работали с TS — не страшно. Это просто язык статического анализа данных, и, разрабатывая на нём, можно всё так же писать на JS.
Переиспользуем код из другой статьи
В предыдущей статье [ссылка на статью] (её полный код лежит на GitHub) мы уже создали часть нужной функциональности: отправку сообщений и запись данных в БД. Обратите внимание: ранее мы писали на JavaScript, а теперь будем на TypeScript. Главное — следите за расширением файлов, в целом ничего не меняется.
Разворачиваем приложение Next.js
Развернуть приложение Next.js можно с помощью документации на сайте https://nextjs.org. Но мы выбрали Shadcn, и эти ребята тесно сотрудничают с Next.js, поэтому предоставили возможность с помощью одной команды развернуть Next.js с Shadcn и настроить всё под себя. Перейдём в официальный источник https://ui.shadcn.com/docs/installation/next и воспользуемся командой, которая за нас всё сделает. Важно: выбирайте опцию с CSS-переменными и поддержкой TypeScript.
Копирование функциональности из прошлой статьи
Заходим в папку components и скачиваем файл SmsForm.js, переименовываем его в SmsForm.tsx, чтобы продолжить работать в TypeScript. Далее в папке api копируем файлы sendSms.js и messages.js и называем их sendSms.ts и messages.ts. Подключение к базе будет таким же, поэтому качаем в корне проекта файл db.js. Обратите внимание на пути подключения и настройте их под себя. После того как мы изменили расширение на ts, в скопированных файлах подсветятся ошибки TypeScript.
Сам TypeScript не вносит новой функциональности, а занимается только проверкой типов данных для лучшей отладки кода. Его можно настроить в конфигурационном файле tsconfig.json: указать, чтобы TS не подсвечивал ошибки или не проверял код. Также не забудьте скачать файл .env и заполнить его данными для подключения к БД, токеном авторизации МТС Exolve и SMS API для отправки сообщений. Если вы всё сделали правильно, то приложение уже может отправлять сообщения.
Компонент Calendar — календарь с сообщениями
Разработаем функциональность для компонента Calendar из Shadcn. Для начала нужно его скачать: находим в библиотеке и следуем инструкции по установке через CLI. Система сама добавит Calendar к нам в проект, а также все входящие в него компоненты Shadcn.
Не удивляйтесь, если в папке components появятся новые компоненты. Уже на этом этапе можно импортировать calendar на страницу и использовать, но мы так делать не будем, чтобы не захламлять код.
Инкапсулируем логику календаря, вынеся его в свой кастомный компонент. Создадим новый компонент в папке components и назовём его CalendarMessage.tsx. Обратите внимание: по соглашению между программистами для удобства желательно называть компоненты React с большой буквы, хоть и Shadcn по умолчанию именует их с маленькой. Это можно отредактировать в конфигурационном файле. Вот документация по его настройке: https://ui.shadcn.com/docs/components-json.
Логика работы календаря
Логика записи исходящих сообщений в БД у нас уже есть в файле api messages.ts. Нам остаётся «постучаться» к этому API, достать оттуда данные о SMS, отфильтровать по дате, выбранной в нашем календаре, и вывести через map все сообщения с помощью условного рендеринга под компонентом calendar. Обратите внимание, что мы преобразовываем дату к строке для сравнения, и не забывайте, что локаль даты может быть разной, как и часовой пояс. Внимательно следите за преобразованием и часовым поясом: эта статья лишь показывает пример реализации.
Содержимое CalendarMessage.tsx
Вставьте следующий код в компонент CalendarMessage.tsx:
import React, { useEffect, useState } from "react";
import { Calendar } from "./calendar";
// Определение интерфейса для объекта сообщения
interface Message {
received_at: string; // Дата получения сообщения в формате строки
sender_number: string; // Номер телефона отправителя
recipient_numbers: string; // Строка с номерами получателей
text: string; // Текст сообщения
}
const CalendarMessage: React.FC = () => {
// Состояние для хранения текущей выбранной даты
const [date, setDate] = useState(new Date());
// Состояние для хранения списка сообщений
const [messages, setMessages] = useState([]);
// Эта функция для загрузки сообщений из API при монтировании компонента
useEffect(() => {
const getMessages = async () => {
const res = await fetch("/api/messages");
const data = await res.json();
setMessages(data); // Сохранение полученных данных в состояние messages
};
getMessages();
}, []);
// Эта функция обрабатывает выбор даты в календаре
const handleDateSelect = (newDate: Date | undefined) => {
if (newDate) {
setDate(newDate); // Обновление состояния date, если выбрана новая дата
}
};
// Фильтрация сообщений по выбранной дате
const filteredMessages = messages.filter((message) => {
const messageDate = new Date(message.received_at).toDateString(); // Преобразование даты сообщения в строку
return messageDate === date.toDateString(); // Сравнение даты сообщения с выбранной датой
});
// Рендер компонента
return (
<>
Сообщения за {date.toLocaleDateString()}
{filteredMessages.length > 0 ? (
{filteredMessages.map((message, index) => (
-
Номер отправителя: {message.sender_number}
Номера получателей:{" "}
{message.recipient_numbers}
Текст сообщения: {message.text}
Получено:{" "}
{new Date(message.received_at).toLocaleString()}
))}
) : (
Нет сообщений за этот день.
// Сообщение, если нет сообщений за выбранный день
)}
>
);
};
export default CalendarMessage;
Остаётся импортировать этот компонент на главную страницу index.tsx.
Компонент Chart — график с отправленными SMS
Подобно установке календаря через Shadcn давайте установим компонент графика Chart. Так же вынесем логику графика в отдельный файл. Создадим в папке components компонент ChartMessage.tsx.
Логика работы графика для отображения количества SMS
Для реализации графика будем вновь использовать API messages.ts, чтобы достать данные с сообщениями. Запрограммируем график на отображение сегодняшней даты плюс два дня назад и два дня вперёд (вы можете указать любой диапазон и логику). Дальше мы должны получить данные по API: сравниваем их с датами в счётчике и при совпадении записываем в ячейку определённой даты количество сообщений. Не забывайте про часовой пояс! Рендерим компонент и наслаждаемся результатом.
Содержимое ChartMessage.tsx
Вставьте следующий код в компонент ChartMessage.tsx:
import React, { useState, useEffect } from "react";
import { Bar, BarChart, CartesianGrid, XAxis, Tooltip } from "recharts";
import { ChartContainer } from "@/components/ui/chart";
// Типы для сообщений и данных графика
interface Message {
id: number;
received_at: string;
}
interface ChartData {
time: string; // дата в формате YYYY-MM-DD
sms: number; // количество сообщений
}
// Конфигурация графика
const chartConfig = {
sms: {
label: "sms",
color: "#2563eb",
},
};
const ChartMessage: React.FC = () => {
// Состояние для данных графика
const [chartData, setChartData] = useState([]);
// Получение данных при монтировании компонента
useEffect(() => {
const fetchData = async (): Promise => {
try {
const response = await fetch("/api/messages");
if (!response.ok) {
throw new Error(`Error fetching messages: ${response.statusText}`);
}
const data: Message[] = await response.json();
updateChartData(data);
} catch (error) {
console.error("Error fetching messages:", error);
}
};
fetchData();
}, []);
// Функция для обновления данных графика
const updateChartData = (data: Message[]): void => {
const today = new Date();
const counts: Record = {};
// Генерация диапазона дат для графика
for (let i = -2; i <= 2; i++) {
const date = new Date(today);
date.setDate(date.getDate() + i);
const dateString = date.toISOString().split("T")[0]; // Преобразование даты в строку YYYY-MM-DD
counts[dateString] = 0; // Инициализация счетчика для каждой даты
}
// Подсчет количества сообщений по датам
data.forEach((msg) => {
const msgDate = msg.received_at.split("T")[0];
if (counts.hasOwnProperty(msgDate)) {
counts[msgDate]++;
}
});
// Создание данных для графика из счетчиков
const newChartData: ChartData[] = Object.keys(counts).map((key) => ({
time: key,
sms: counts[key],
}));
setChartData(newChartData);
};
// Отрисовка компонента
return (
);
};
export default ChartMessage;
Остаётся импортировать его на главную страницу index.tsx.
Результат
Над дизайном вы при желании поработаете сами, а основные функции мы добавили вместе — можно рассылать SMS и просматривать, что и в каком количестве было отправлено. Мы разместили весь код на GitHub.
Подводные камни компонента Chart
Если в консоли высветится ошибка — это нормально. Уже целый год разработчики серьёзной технологии Shadcn пытаются перейти с пропсов по умолчанию на пропсы кастомных компонентов. Вывод в консоли просто предупреждает программистов, что как только разрабам это удастся сделать, при обновлении, возможно, придётся залезть в код и кое-что допилить. Пожелаем им удачи.
Второй подводный камень — часовой пояс. База данных может иметь часовой пояс и преобразовывать даты по-своему. Можно следить за этим и устанавливать везде равные часовые пояса или преобразовывать дату во время выполнения кода. В компоненте календаря удалось вывести дату как строку, но в компоненте графика это оказалось сложнее: пришлось преобразовывать внутри. Как следствие, компонент календаря и компонент графика выводят сообщения в разных часовых поясах. Для продуктивной среды такой вариант не совсем подойдёт, а вот в качестве статьи очень даже.
Заключение
В статье реализована функциональность работы с абонентом через API-платформу МТС Exolve — это надёжный способ интеграции. В сочетании с готовыми компонентами задача становится вполне подъёмной. Тщательно планируйте логику работы приложения, задавайте ему стили и архитектуру, и у вас всё получится. Также не забывайте про правильную настройку и обработку даты. А если вы ещё не знаете TypeScript, то самое время его изучить — язык не сложный, а пользы от него предостаточно.