Real-time Restaurant POS на Next.js: как я решал проблемы синхронизации, ошибок и UX с клавиатурой

#Next.js#real-time#POS#Pusher#state management#keyboard UX

Пару месяцев назад я взялся за проект — real-time POS для ресторана, с учетом кухни, официантов и курьеров. На стек взял Next.js + TypeScript + Tailwind, а для мгновенной синхронизации — Pusher. Казалось бы, классика real-time, но когда зашли в режим rush hour с кучей одновременных обновлений, столкнулся с парой важных нюансов, которые часто упускают из виду.

Расскажу, как я решал три главные проблемы: синхронизацию состояния со стороны фронта, логи отклонений заказов (rejection logs) и UX для максимально быстрой работы с клавиатуры.


Синхронизация состояния и отклонения заказов: не всё так просто

Идея была стандартной: когда на кухне заканчивается ингредиент, шеф может reject заказ с объяснением — например, “Out of stock”. Это мгновенно должно отразиться у официантов, админов и в истории заказов.

Pusher отлично справляется с пушами, но real-time — это не всегда real-ideal. В rush hour с десятками параллельных обновлений я столкнулся с двумя главными болью:

Решение у меня получилось такое:

  1. Версионирование статусов: каждый заказ хранит поле version или updatedAt. При получении пуша с обновлением, фронт проверяет, не старее ли пришедшие данные текущего состояния. Если да — игнорирует, если нет — апдейтит.

  2. Оптимистичные обновления с откатом: когда официант меняет статус, UI сразу меняет состояние (optimistic update) и запускает запрос на сервер. Если сервер в ответе говорит “reject”, фронт откатывает изменения и показывает уведомление.

  3. Логирование отклонений: все rejection-сообщения не только пушатся всем участникам, но и пишутся в отдельный лог с таймстампами. Это помогает администратору разобраться с проблемами и служит audit trail.

В итоге интерфейс стал максимально отзывчивым, без тупых задержек и несостыковок. Но это потребовало дополнительной логики и хорошего контроля версий данных.


Клавиатурные хоткеи и минимизация кликов — life hack для официанта

В ресторане каждая секунда на счету. Переключение между мышкой и клавиатурой — потеря времени и ритма. Поэтому я решил сделать интерфейс, полностью управляемый с клавиатуры.

Вот что помогло:

На практике это сильно ускоряет работу официантов. У меня был скепсис насчет сложности обучения, но после пары дней все вошло в привычку.


Пример: обработка rejection с версиями и откатом

type OrderStatus = "pending" | "accepted" | "rejected";

interface Order {
  id: string;
  status: OrderStatus;
  version: number;
  rejectionReason?: string;
}

const [order, setOrder] = React.useState<Order>(initialOrder);

async function updateOrderStatus(newStatus: OrderStatus, reason?: string) {
  // Оптимистично обновляем UI
  const optimisticVersion = order.version + 1;
  setOrder({ ...order, status: newStatus, version: optimisticVersion, rejectionReason: reason });

  try {
    const res = await fetch(`/api/orders/${order.id}/status`, {
      method: "POST",
      body: JSON.stringify({ status: newStatus, reason }),
    });
    const data = await res.json();

    if (data.version < optimisticVersion) {
      // Сервер откатил изменения
      setOrder(prev => prev); // возвращаем состояние, можно дополнительно показать ошибку
      alert("Статус не обновился: " + data.message);
    } else {
      // Подтверждаем версию с сервера
      setOrder(data.order);
    }
  } catch (e) {
    // Ошибка сети, откатываем
    setOrder(prev => prev);
    alert("Ошибка обновления статуса");
  }
}

// При пуше от Pusher:
function onPusherUpdate(updatedOrder: Order) {
  if (updatedOrder.version > order.version) {
    setOrder(updatedOrder);
  }
}

Кому это зайдёт, а кому нет?

Если вы строите real-time дашборд с кучей параллельных апдейтов, вам точно пригодится паттерн с версиями и оптимистичными обновлениями. Pusher и аналоги — удобны, но на фронте нужно держать логику для согласованности данных.

Если вы делаете интерфейс для людей, которым важно скорость ввода — keyboard-first UX и хоткеи — must-have. Но будьте готовы вложиться в обучение и тестирование, чтобы не усложнить жизнь пользователям.

В целом, на практике такой подход позволяет сделать POS-систему, которая выдерживает реальные нагрузки и не раздражает персонал. Следующий шаг — добавить agentic workflows для автоматических действий на повторяющиеся сценарии, но это уже тема для отдельного разговора.


Если интересно, могу поделиться исходниками и подробнее обсудить, как наладить state reconciliation в более сложных real-time системах.


Источник: https://www.reddit.com/r/nextjs/comments/1wlc65j/built_a_realtime_restaurant_pos_with_nextjs/