new-term-radar
신규 AI 용어를 매일 추적, X·Hugging Face·GitHub 등에서 모델·제품·레포 이름을 빠르게 수집
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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 с планом и статусом.