new-term-radar
Daily radar for new AI terms: names of new models, products and repos, caught on X, Hugging Face, GitHub and sitemaps before keyword tools list them.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Сквозная аналитика рекламы до реальной продажи: Meta Ads и TikTok Ads → лид → пробный → продажа → выручка. Multi-tenant SaaS на Next.js + Supabase. Текст исходного ТЗ — в PROMPT.md. Порядок выката и что проверять — в DEPLOY.md. SUPABASE_SERVICE_ROLE_KEY нужен только админ-панели для создания компаний и учётных записей директоров. Файл, который его читает, помечен server-only — сборка упадёт при попытке импортировать его в клиентский компонент.檢視完整 README ↗
Сквозная аналитика рекламы до реальной продажи: Meta Ads и TikTok Ads → лид → пробный → продажа → выручка. Multi-tenant SaaS на Next.js + Supabase.
Текст исходного ТЗ — в PROMPT.md.
| Адрес | |
|---|---|
| Сайт | https://lidera-dashboard.vercel.app |
| Репозиторий | github.com/dauletkiyalbek7/lidera-dashboard (деплой на каждый push в main) |
| База и Auth | Supabase «Lidera Final» (xnfqsoruxkjhekdklxot) |
| Бот | @lidera_dash_bot, вебхук /api/telegram/webhook |
Порядок выката и что проверять — в DEPLOY.md.
npm install
cp .env.example .env.local # заполнить значения, см. ниже
npm run dev # http://localhost:3000
| Переменная | Где взять | Попадает в браузер |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase → Lidera Final → Project Settings → API | да |
NEXT_PUBLIC_SUPABASE_ANON_KEY | там же, ключ anon | да |
SUPABASE_SERVICE_ROLE_KEY | там же, ключ service_role | никогда |
NEXT_PUBLIC_SITE_URL | адрес сайта (для писем восстановления пароля и OG) | да |
TELEGRAM_BOT_TOKEN | @BotFather → токен бота | никогда |
NEXT_PUBLIC_TELEGRAM_BOT_USERNAME | имя бота без @ — нужно в ссылках-приглашениях | да |
TELEGRAM_WEBHOOK_SECRET | любая длинная случайная строка (openssl rand -hex 32) | никогда |
META_ACCESS_TOKEN | Meta Business → пользователь-системы → токен с правом ads_read | никогда |
META_API_VERSION | версия Graph API, по умолчанию v23.0 | нет |
SUPABASE_SERVICE_ROLE_KEY нужен только админ-панели для создания компаний и учётных
записей директоров. Файл, который его читает, помечен server-only — сборка упадёт при
попытке импортировать его в клиентский компонент.
| Роль | Логин | Пароль | Что показывает |
|---|---|---|---|
| SUPER_ADMIN | [email protected] | LideraAdmin2026! | /admin — все компании |
| DIRECTOR | [email protected] | DemoDirector2026! | воронка с пробным занятием (онлайн-школа) |
| DIRECTOR | [email protected] | DemoParfum2026! | прямая продажа без пробных (парфюмерия) |
Пароли демонстрационные — смените их перед публичным запуском.
Проект Supabase — Lidera Final (xnfqsoruxkjhekdklxot, регион eu-central-1).
Старый проект lidera не используется.
supabase/
migrations/
20260812100000_core_schema.sql компании, профили, подписки
20260812100100_ads_schema.sql кабинеты, кампании, группы, объявления, креативы, метрики
20260812100200_crm_schema.sql лиды, пробные, продажи, чеки, интеграции, журнал
20260812100300_rls.sql RLS и политики
20260812110000_harden_rls_helpers.sql перенос служебных функций в схему private
20260812120000_company_funnel_type.sql тип воронки компании
20260812130000_lead_status_pipeline.sql расширенные статусы лида
20260812140000_simplify_lead_statuses.sql убраны «целевой» и «нецелевой»
20260812150000_employees.sql сотрудники компании и ответственный за лида
20260813100000_telegram_invites_shifts.sql приглашения в бот и рабочие смены
20260813110000_lead_distribution.sql настройки раздачи и журнал назначений
20260813120000_shift_modes_attendance.sql режимы смены, геолокация офиса, табель
20260813130000_employee_shift_overrides.sql личный режим и график сотрудника
20260813140000_work_schedule.sql конец рабочего дня и рабочие дни недели
20260813150000_company_currency.sql валюта отчётов компании
20260813160000_campaign_whatsapp_number.sql номер WhatsApp у кампании
20260813170000_creative_media.sql видео, заголовок и текст креатива
seed.sql две демо-компании: с пробными и без
Применить на чистую базу:
npx supabase link --project-ref xnfqsoruxkjhekdklxot
npx supabase db push
npx supabase db execute --file supabase/seed.sql
Каждая строка данных клиента несёт company_id. Доступ ограничен политиками Row Level
Security, а не фильтрами в интерфейсе:
private.current_company_id() — компания текущего пользователя;private.is_super_admin() — платформенный администратор видит всё;private.can_write() — право записи внутри своей компании.Функции живут в схеме private, поэтому не доступны через /rest/v1/rpc, и объявлены
security definer — иначе политика на profiles, читающая profiles, ушла бы в рекурсию.
Проверено на живой базе: директор видит только свою компанию, не может создать компанию
(403) и не может вставить строку с чужим company_id (ошибка политики RLS).
src/
app/
page.tsx публичный лендинг
(marketing)/ контакты, политика конфиденциальности
login/ forgot-password/ reset-password/ авторизация (Server Actions)
auth/confirm/ auth/signout/ обмен токена из письма, выход
dashboard/ кабинет компании: 11 разделов
admin/ платформенная админка
components/
landing/ auth/ app/ ui/ charts/
lib/
auth.ts requireSession / requireCompanySession / requireSuperAdmin
queries.ts данные кабинета (под RLS)
admin-queries.ts данные админки
metrics.ts CPL, CAC, ROAS, ROI, конверсия — в одном месте
lead-status.ts статусы лида: набор, стадии, правила воронки
employee-role.ts роли сотрудников и кому раздаются лиды
format.ts деньги, числа, даты (валюта — тенге)
period.ts диапазон дат
supabase/ клиенты browser / server / admin + типы
proxy.ts обновление сессии и защита закрытых маршрутов
Три уровня контроля доступа, каждый следующий не полагается на предыдущий:
proxy.ts — есть ли сессия;lib/auth.ts в layout-ах — роль и статус компании;Не у всех клиентов есть промежуточный шаг «пробное занятие»: онлайн-школа ведёт лид
через пробный урок, товарный бизнес продаёт сразу. Поле companies.funnel_type
переключает кабинет между двумя моделями:
trial | direct | |
|---|---|---|
| Воронка | Лид → Пробный → Продажа | Лид → Контакт → Продажа |
| KPI и колонка в таблице креативов | «Пробные» | «Дозвон» |
| Раздел «Пробные» в меню | есть | скрыт |
| Статус «Пробный» у лида | доступен | недоступен (проверка и в форме, и на сервере) |
Тип задаёт администратор при создании компании и меняет директор в настройках.
Промежуточный шаг считается в одном месте — middleStepValue() в lib/metrics.ts,
поэтому новую модель воронки можно добавить, не трогая страницы.
Менеджер оценивает лид результатом контакта, а не абстрактной «работой». Набор,
подписи, стадии и правила воронки лежат в одном файле — lib/lead-status.ts;
оттуда их берут выпадающие списки, валидация серверных действий и сводки.
| Статус | Смысл | Стадия |
|---|---|---|
new | заявка пришла, её ещё не трогали | новые |
no_answer | «Игнор» — не отвечает | игнор |
contacted | «Дозвон» — разговор состоялся | в работе |
in_progress | переговоры продолжаются | в работе |
thinking | взял паузу на решение | в работе |
trial | записан на пробное (только воронка trial) | в работе |
sale | оплата прошла | купили |
rejected | поговорили, покупать не будет | потеряны |
Шаг воронки «обработан» = был живой контакт (isReached). Отказ сюда входит —
с человеком поговорили; игнор не входит. Поэтому продажи всегда вложены
в обработанных, и воронка не «расширяется» на среднем шаге.
Платформу видит только директор компании (и платформенный администратор).
У сотрудника нет учётной записи и входа в кабинет: он заведён строкой в
employees, а работать будет через Telegram-бот — под него уже есть поля
telegram_user_id и telegram_username, привязка появится на следующем этапе.
| Роль | Что делает | Получает лиды |
|---|---|---|
rop | руководитель отдела продаж | нет |
manager | принимает лиды, дозванивается, ведёт по воронке | да |
salesperson | проводит пробное и закрывает продажу (только воронка trial) | нет |
Увольнение мягкое: status = 'fired' + дата, строка остаётся — отчёты за прошлые
периоды не должны меняться задним числом. Активные лиды уволенного освобождаются,
чтобы не зависли на том, кто уже не работает.
Вход сотрудника в бот — по одноразовой ссылке t.me/<бот>?start=<токен>: директор
жмёт «Пригласить», платформа выдаёт ссылку на 48 часов и гасит прежние ссылки
этого человека. Смена (shifts) — выключатель для будущей авто-раздачи: частичный
уникальный индекс не даёт открыть две смены одному сотруднику.
Раздел «Команда» показывает по каждому за выбранный период: лиды, дозвон, продажи и выручку. Выручка считается по лидам, где человек указан ответственным. В таблице лидов есть столбец «Ответственный» — пока назначается вручную, авто-раздача встанет на это же поле.
Раздел «Лиды» показывает разбор по статусам и четыре показателя отдела: всего, дозвонились, купили и «ждут первого касания» — новые лиды старше суток. Сводка считается отдельным запросом по всему периоду, а не по видимым 200 строкам.
Лид не должен лежать без хозяина, и не должен висеть на том, кто его не берёт.
Правила — в lib/lead-distribution.ts, три настройки у каждой компании
(«Настройки → Раздача лидов»):
| Настройка | По умолчанию | Смысл |
|---|---|---|
auto_assign | включена | раздавать ли автоматически |
max_open_leads | 15 | сколько активных лидов может висеть на менеджере |
sla_minutes | 10 | через сколько бот напомнит про нетронутый лид |
max_touches | 5 | после скольких безрезультатных попыток предложить закрыть лид |
Как это работает:
max_open_leads менеджер новых лидов не получает;Вход сотрудника. Директор выдаёт его в «Команде» кнопкой «Выдать вход»: задаёт почту-логин и пароль и передаёт их лично — почтового ящика у менеджера может не быть, и письмо-приглашение до него не дойдёт. Пароль после сохранения не показывается: в базе его нет в открытом виде, сотрудник меняет его сам в разделе «Мой профиль». Там же он сам подключает Telegram — пересылать ссылку через директора больше не нужно.
Что сотрудник видит, решают политики базы, а не меню: менеджеру доступны лиды,
где он ответственный, продажнику — его уроки. Проверено запросом от их имени:
менеджер не видит заявку без ответственного, продажник не видит чужих лидов.
Ключевые функции — private.current_employee_id() и
private.sees_whole_company(); разделы руководства закрыты ещё и
requireFullAccess() в lib/auth.ts.
Онлайн-урок. Пробный урок покупают и проводят по видеосвязи, поэтому он не
раздаётся очередью: менеджер согласовывает время с клиентом и в форме записи
выбирает продажника, свободного именно в этот час (занятые видны, но выбрать их
нельзя — занятость перепроверяется и на сервере). Продажнику сразу уходит
карточка в Telegram, за час до начала — напоминание. Исход он отмечает кнопкой:
Проведён · Купил курс · Не одобрил · Не вышел на связь · Отменён. «Купил» и
«Не одобрил» двигают заодно и лид — иначе он навсегда остаётся на шаге
«Пробный». Набор статусов ведётся в lib/trial-status.ts.
Касания. Не дозвонился — менеджер жмёт «Игнор», и бот спрашивает, когда
перезвонит: через час, вечером, завтра, через неделю. Срок пишется в
lead_touches, в назначенное время бот напоминает карточкой лида. Просроченные
обещания видит директор в «Команде» (колонки «Касания» и «Просрочено»). Если лид
так и лежит нетронутым дольше sla_minutes, бот подталкивает — но не чаще
одного раза за тот же срок и всегда тому же менеджеру.
Раздача и напоминания запускаются при создании лида, при открытии смены и раз
в минуту из самой базы: pg_cron + pg_net дёргают POST /api/cron/distribute.
Планировщик Vercel для этого не годится — на бесплатном тарифе он срабатывает
раз в сутки, а обещание «перезвоню через час» надо напомнить через час.
Отдельной переменной для доступа к этому адресу нет намеренно: ключ — это
sha256 от сервисного ключа Supabase, который и так есть у обеих сторон.
В базе (Vault) лежит только хеш, сам ключ по сети не ходит.
Каждое назначение пишется в lead_assignments с причиной (auto, manual,
fired). Без журнала не разобрать спор «мне лид не приходил» и не посчитать
честную очередь. Причина sla осталась в справочнике ради старых записей —
новые с ней не появляются.
Режим смены задаётся компании («Настройки → Смены и офис») и при необходимости переопределяется каждому сотруднику лично («Команда → График»). Это не роскошь: в одной компании обычно есть и офисный отдел, и менеджеры из дома.
| Режим | Что значит |
|---|---|
| Без смены | лиды идут менеджерам всегда, отмечаться не нужно |
| По кнопке | лиды получает только тот, кто нажал «Я на смене» — для удалёнки |
| По кнопке и геолокации | смену можно открыть только рядом с офисом |
В режиме geo бот просит отправить геолокацию — Telegram отдаёт её только по
нажатию кнопки, тихо определить место сотрудника нельзя, и это правильно.
Расстояние считается по формуле гаверсинуса (lib/geo.ts) и записывается в
смену: спор «я был на месте» решается фактом. Радиус меньше 50 метров поставить
нельзя — точность GPS в городе хуже, и сотрудник в офисе получал бы отказ.
График работы — это рабочие дни недели, начало и конец дня и допустимое
опоздание. Он задаётся компании и может быть личным у любого сотрудника: в
карточке «Команда → График» переключатель «Как в компании / Свой». Редактор
один и тот же на обоих экранах (components/app/work-schedule.tsx): дни
выбираются кнопками с готовыми 5/2, 6/1 и 7/0, время — часами, и рядом сразу
видно длину смены и недельную норму часов. Конец раньше начала — это ночная
смена через полночь, а не ошибка.
Опоздание считается по местному времени компании (timezone), а не по времени
сервера, и только в рабочий по графику день: вышел в свой выходной — отметки
«опоздал» не будет.
«Как в компании» пишет в базу NULL — так общее правило меняется в одном месте и
доходит до всех, у кого нет исключения. Действующие правила собирает
resolveShiftRules(); бот, раздача и интерфейс берут их оттуда, а не считают
каждый по-своему.
Табель (attendance) — одна отметка на сотрудника в день. «На смене» и
«Опоздал» ставит система по фактическим сменам; больничный, отпуск, выходной и
удалёнку проставляет директор в разделе «Посещение». Ручная отметка сильнее
автоматической: открытие смены не затирает проставленный больничный.
Набор статусов компания выбирает из общего справочника — так отчёты по разным
компаниям остаются сравнимыми.
Все разделы читают период из адреса (?period=today, ?from=…&to=…) через
lib/period.ts. «Сегодня» считается в часовом поясе компании
(companies.timezone, по умолчанию Asia/Almaty), а не по серверу: Vercel живёт
по UTC, и с полуночи до пяти утра по Алматы «сегодняшний» день сервера — это ещё
вчера. По умолчанию открывается сегодняшний день.
Раздел «Реклама» считает из ad_metrics — дневных строк, которые отдаёт рекламный
кабинет. Наполняет их lib/meta-sync.ts: каждые два часа (pg_cron → /api/cron/meta-sync,
25 */2 * * *) платформа сама ходит в Meta Marketing API и перезабирает последние
30 дней целиком — раз в сутки было бы мало, потому что кабинет по умолчанию
открывается на сегодняшнем дне, а сегодняшний расход обязан быть свежим — Meta уточняет вчерашние цифры ещё пару дней, поэтому
дописывать их было бы неверно. Токен даёт только чтение (ads_read). Для кампаний с целью «переписки» поле leads хранит начатые переписки:
для такой рекламы написавший человек и есть лид.
Кампания несёт номер WhatsApp (campaigns.whatsapp_number), поэтому в разделе
видно, на какой номер сколько людей написало и почём. Номер берётся из
настроек группы объявлений (promoted_object.whatsapp_phone_number); если Meta
его не отдала, показываются последние цифры из названия кампании в виде «…8045» —
достраивать номер догадкой нельзя, правдоподобная выдумка хуже пропуска.
Расход приходит в валюте рекламного кабинета (ad_metrics.currency), а кабинет
показывает деньги в валюте компании. Пересчёт — по курсу Нацбанка РК на день
расхода (exchange_rates, наполняется той же синхронизацией). Подписать доллары
знаком тенге без пересчёта нельзя: это расхождение в сотни раз.
Креативы синхронизируются вместе с метриками: на уровне объявления видно, какой
ролик принёс переписки. Раздел «Креативы» — список того, что за период работало
(тратило бюджет или приводило людей); нажатие на строку открывает отдельный
экран с проигрывателем и всеми цифрами. Плееров в списке нет намеренно: десяток
роликов на одной странице грузится долго и мешает сравнивать. Ссылки Meta подписаны и живут недолго,
поэтому храним идентификатор ролика, а /api/creatives/[id]/media получает
свежий адрес на сервере и отправляет туда браузер: токен наружу не уходит, а
чужой креатив не отдаётся — строка читается под сессией пользователя, через RLS.
Обложку берём в исходном размере: превью 64×64 Meta режет по центру и обрезает
людям лица.
Валюта — своя у каждой компании (companies.currency): Meta списывает у одного
клиента тенге, у другого доллары, и подписать долларовый расход знаком тенге
значит соврать. formatMoney принимает валюту, страницы берут её из компании.
Один компонент DateRangePicker на всех разделах с метриками: девять пресетов
(сегодня, вчера, 7/30/90 дней, эта и прошлая неделя, этот и прошлый месяц) плюс
произвольный диапазон, который выбирается двумя кликами по календарю.
Состояние живёт в адресе страницы — ?period=last_month или
?from=2026-07-01&to=2026-07-15, — поэтому ссылку на конкретный период можно
переслать коллеге. Некорректные значения молча заменяются диапазоном по умолчанию.
Цвета серий (--color-series-revenue, --color-series-spend) подобраны под тёмную
поверхность и проверены на различимость при дальтонизме и контраст к фону. Они намеренно
отделены от лаймового акцента интерфейса: акцент — для кнопок, серии — для данных.
Выручка и расход лежат на одной шкале в тенге; второй оси Y нет — при разных шкалах она создаёт корреляцию, которой в данных не существует.
NEXT_PUBLIC_SITE_URL —
боевой адрес, например https://lidera.kz).https://<домен>/auth/confirm в Redirect URLs — иначе не заработает восстановление пароля.Рекомендуется включить Supabase → Authentication → Password protection → проверку паролей по базе утечек: это единственное оставшееся предупреждение линтера безопасности, и оно снимается только в интерфейсе Supabase.
Схема уже рассчитана на следующие этапы и не потребует переписывания:
ad_accounts → campaigns → ad_sets → ads → creatives
повторяет модель обоих API; внешние идентификаторы хранятся в external_id,
синхронизация пишет в ad_metrics.receipts со статусами проверки, полем source
и ocr_raw для сырого ответа распознавания.MANAGER и EMPLOYEE уже разрешены схемой и учтены в политиках.subscriptions с планом и статусом.