Как сделать интерактивную панель для отправки 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<Date>(new Date());   // Состояние для хранения списка сообщений   const [messages, setMessages] = useState<Message[]>([]);     // Эта функция для загрузки сообщений из 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 (     <>       <div className="w-full flex items-center justify-center">         <Calendar           mode="single" // Режим выбора одной даты           selected={date} // Текущая выбранная дата           onSelect={handleDateSelect} // Функция, вызываемая при выборе даты           className="rounded-md border" // Классы стилей для календаря         />       </div>       <div className="max-w-4xl mx-auto mt-10">         <h2 className="text-lg font-semibold">           Сообщения за {date.toLocaleDateString()}         </h2>         {filteredMessages.length > 0 ? (           <ul>             {filteredMessages.map((message, index) => (               <li key={index} className="mb-2 p-2 border rounded">                 <p>                   <strong>Номер отправителя:</strong> {message.sender_number}                 </p>                 <p>                   <strong>Номера получателей:</strong>{" "}                   {message.recipient_numbers}                 </p>                 <p>                   <strong>Текст сообщения:</strong> {message.text}                 </p>                 <p>                   <strong>Получено:</strong>{" "}                   {new Date(message.received_at).toLocaleString()}                 </p>               </li>             ))}           </ul>         ) : (           <p>Нет сообщений за этот день.</p> // Сообщение, если нет сообщений за выбранный день         )}       </div>     </>   ); };   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<ChartData[]>([]);     // Получение данных при монтировании компонента   useEffect(() => {     const fetchData = async (): Promise<void> => {       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<string, number> = {};       // Генерация диапазона дат для графика     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 (     <div className="max-w-4xl mx-auto mt-10">       <ChartContainer config={chartConfig} className="h-[200px] w-full">         <BarChart accessibilityLayer data={chartData}>           <CartesianGrid vertical={false} />           <XAxis dataKey="time" />           <Tooltip />           <Bar dataKey="sms" fill={chartConfig.sms.color} radius={4} />         </BarChart>       </ChartContainer>     </div>   ); };   export default ChartMessage;

Остаётся импортировать его на главную страницу index.tsx.

Результат

Над дизайном вы при желании поработаете сами, а основные функции мы добавили вместе — можно рассылать SMS и просматривать, что и в каком количестве было отправлено. Мы разместили весь код на GitHub.

Подводные камни компонента Chart

Если в консоли высветится ошибка — это нормально. Уже целый год разработчики серьёзной технологии Shadcn пытаются перейти с пропсов по умолчанию на пропсы кастомных компонентов. Вывод в консоли просто предупреждает программистов, что как только разрабам это удастся сделать, при обновлении, возможно, придётся залезть в код и кое-что допилить. Пожелаем им удачи.

Второй подводный камень — часовой пояс. База данных может иметь часовой пояс и преобразовывать даты по-своему. Можно следить за этим и устанавливать везде равные часовые пояса или преобразовывать дату во время выполнения кода. В компоненте календаря удалось вывести дату как строку, но в компоненте графика это оказалось сложнее: пришлось преобразовывать внутри. Как следствие, компонент календаря и компонент графика выводят сообщения в разных часовых поясах. Для продуктивной среды такой вариант не совсем подойдёт, а вот в качестве статьи очень даже.

Заключение

В статье реализована функциональность работы с абонентом через API-платформу МТС Exolve — это надёжный способ интеграции. В сочетании с готовыми компонентами задача становится вполне подъёмной. Тщательно планируйте логику работы приложения, задавайте ему стили и архитектуру, и у вас всё получится. Также не забывайте про правильную настройку и обработку даты. А если вы ещё не знаете TypeScript, то самое время его изучить — язык не сложный, а пользы от него предостаточно.


ссылка на оригинал статьи https://habr.com/ru/articles/857992/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *