Сегодня разработаем панель, через которую сможем отправлять 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/
Добавить комментарий