Розробка списку обраного (Wishlist) для інтернет-магазину

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка списку обраного (Wishlist) для інтернет-магазину
Проста
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розроблення списку бажаного (Wishlist) для інтернет-магазину

Wishlist — список товарів, які користувач відмітив для себе: «хочу купити пізніше», «стежу за ціною», «подарунковий список». Це ненульовий за складністю функціональний блок: стан кнопки синхронізований глобально, список працює без авторизації через localStorage, а при вході мержится з серверним. Плюс сповіщення про зниження цін, шаринг списку, сегментація в маркетингу.

Сценарії використання

Три основні сценарії, кожен з різними технічними вимогами:

«Куплю потім» — швидке збереження для повернення. Не потребує авторизації, достатньо localStorage.

«Стежу за ціною» — користувач хоче сповіщення при зниженні ціни. Потребує email/push + авторизований аккаунт.

«Подарунковий список» (Gift Registry) — публічний чи ліночечний список, яким діляться з іншими. Потребує серверне збереження + унікальний URL.

Збереження та синхронізація

Анонімний користувач: список в localStorage. При завантаженні сторінки — ініціалізація store з localStorage.

Авторизований користувач: список в БД, localStorage як кеш. При авторизації — merge:

async function mergeWishlists(localItems: number[], userId: number) {
  const serverItems = await api.getWishlist(userId);
  const merged = [...new Set([...serverItems, ...localItems])];
  await api.syncWishlist(userId, merged);
  localStorage.removeItem('wishlist');
  return merged;
}

Схема БД:

wishlists (
  id, user_id, name, slug,
  is_public BOOLEAN DEFAULT FALSE,
  share_token VARCHAR(32),
  created_at
)

wishlist_items (
  id, wishlist_id, product_id, variant_id,
  note TEXT,
  added_at TIMESTAMPTZ,
  price_at_addition NUMERIC
)

Один користувач може мати кілька вишлистів (основний + «для спальні» + «подарунковий»). Для більшості магазинів достатньо одного списку на користувача.

Кнопка «В избранное»

Іконка серця (чи закладки) на карточці товара в листингу та на сторінці товара. Два стани: пуста / заповнена, з анімацією переходу.

function WishlistButton({ productId }: { productId: number }) {
  const { isInWishlist, toggle, isLoading } = useWishlist(productId);

  return (
    <button
      onClick={() => toggle(productId)}
      disabled={isLoading}
      aria-label={isInWishlist ? 'Убрати з избраного' : 'Додати в избране'}
      className={cn(
        'p-2 rounded-full transition-colors',
        isInWishlist ? 'text-red-500' : 'text-gray-400 hover:text-red-400'
      )}
    >
      <HeartIcon filled={isInWishlist} className="w-5 h-5" />
    </button>
  );
}

function useWishlist(productId: number) {
  const store = useWishlistStore();
  const [isLoading, setIsLoading] = useState(false);

  const toggle = async (id: number) => {
    setIsLoading(true);
    try {
      if (store.has(id)) {
        store.remove(id);
        if (isAuthenticated) await api.removeFromWishlist(id);
      } else {
        store.add(id);
        if (isAuthenticated) await api.addToWishlist(id);
      }
    } finally {
      setIsLoading(false);
    }
  };

  return { isInWishlist: store.has(productId), toggle, isLoading };
}

Оптимістичний UI: оновлюємо стан в store відразу (без очікування API). Якщо запит до сервера упав — відкатуємо через try/catch. Користувач видит мгновенну реакцію.

Сторінка вишлиста

Список избраного — окрема сторінка в особистому кабінеті (/account/wishlist) чи публічна сторінка при шарингу (/wishlist/{slug}).

Інтерфейс сторінки:

  • Грід товарів — ті ж карточки, що в каталозі, з кнопкою «Убрати»
  • Фільтр: по наявності, по даті додавання, по зниженню ціни
  • Сортування: по даті додавання, по ціні (зростання/убування), по зміні ціни
  • «Додати все в корзину» — batch-операція, додає доступні товари
  • Статус наявності: «Нема в наявності» — візуально відмічається, але залишається в списку

Для кожного товару показуємо price_at_addition vs поточна ціна — відразу видно, виросла чи знизилась ціна з моменту додавання. Зниження ціни — зі значком «▼ −12%».

Сповіщення про зниження ціни

Користувач може підписатись на сповіщення про зміну ціни товара в вишлисте:

price_alerts (
  id, user_id, product_id,
  threshold_type,        -- 'any_drop' | 'percent_drop' | 'target_price'
  threshold_value,
  is_active BOOLEAN,
  last_notified_at
)

Процес відправлення сповіщень (запускається раз на годину чи при оновленні ціни):

foreach ($alerts as $alert) {
    $currentPrice = $alert->product->price;
    $shouldNotify = match ($alert->threshold_type) {
        'any_drop'      => $currentPrice < $alert->product->previous_price,
        'percent_drop'  => ($currentPrice / $alert->product->previous_price - 1) <= -$alert->threshold_value / 100,
        'target_price'  => $currentPrice <= $alert->threshold_value,
    };

    if ($shouldNotify && $alert->last_notified_at < now()->subDays(3)) {
        Mail::to($alert->user)->queue(new PriceDropNotification($alert->product, $currentPrice));
        $alert->update(['last_notified_at' => now()]);
    }
}

Обмеження частоти сповіщень (last_notified_at < now()->subDays(3)) — щоб не спамити при волатильних цінах.

Шаринг вишлиста

При включенні «Поділитись списком» — генерується share_token:

$wishlist->update([
    'is_public' => true,
    'share_token' => Str::random(32),
]);
return "/wishlist/{$wishlist->share_token}";

Публічна сторінка вишлиста: гість видит товари, може додати будь-який в свою корзину, але не може редагувати список. Ідеально для подарункових списків (дні народження, весілля).

Кнопка «Скопіювати посилання» + кнопки «Поділитись у Telegram / WhatsApp» з передзаповненим текстом.

Інтеграція з email-маркетингом

Wishlist — цінний сегмент для персоналізованих розсилок:

  • «У вашому избраному розпродажа» — при старті акції перевіряємо перетин товарів в вишлистах з розпродажними товарами
  • «Товар з вашого избраного закінчується» — остаток < 3 шт.
  • «Ви давно не заходили — ось що змінилось у вашому списку» — реактивационное письмо

Реалізація: при подієї (старт акції / зміна остатку) — асинхронна задача в черзі, яка збирає користувачів з цим товаром в вишлисте та відправляє персоналізовані листи.

Счітчик на іконці вишлиста

У навігації — іконка серця з бейджем (кількість товарів у списку). Бейдж оновлюється миттєво через store, не через API-запит при кожному рендері.

function WishlistNavIcon() {
  const count = useWishlistStore(state => state.items.length);
  return (
    <div className="relative">
      <HeartIcon className="w-6 h-6" />
      {count > 0 && (
        <span className="absolute -top-1 -right-1 bg-red-500 text-white
                         text-xs rounded-full w-4 h-4 flex items-center justify-center">
          {count > 99 ? '99+' : count}
        </span>
      )}
    </div>
  );
}

SEO-аспекти

Особисті вишлисти (/account/wishlist) — закрити авторизацією, не індексуються. Публічні shared-вішлисти — noindex за замовчуванням, якщо не реалізована редакційна концепція «колекцій» з унікальним контентом.

Аналітика

  • Які товари частіше додають в избране — сигнал популярності, альтернатива sales_count для новинок
  • Wishlist-to-purchase conversion rate: % користувачів, які купили товар з вишлиста
  • Середнє час від додавання в вишлист до покупки — допомагає налаштувати тайминг email-триггерів

Терміни

  • Базовий wishlist (localStorage, кнопка на карточці, сторінка списку без авторизації): 2–4 робочих дня
  • З серверним збереженням, merge при авторизації, сповіщеннями про ціну: 1.5–2.5 тижні
  • Шаринг вишлиста, кілька списків, інтеграція з email-маркетингом: 2.5–4 тижні