Compare commits
136 Commits
v1.0
..
39e2102a18
| Author | SHA1 | Date | |
|---|---|---|---|
| 39e2102a18 | |||
| 3e2c70dd51 | |||
| a239607360 | |||
| 6317dade9e | |||
| 1aa99b9def | |||
| b6d555ab3b | |||
| f8aa27533c | |||
| 0a1425301d | |||
| 06b8d9f64b | |||
| 1cde567dbe | |||
| 0e67a53122 | |||
| 7bf3935c81 | |||
| bb6c806cdd | |||
| 9fbb7aea6c | |||
| 2436f77de5 | |||
| a9869bfac8 | |||
| 4b300f3466 | |||
| a8cd2cd4d4 | |||
| 2ea9c6fda0 | |||
| ec011ab7fd | |||
| 27027b134f | |||
| 04e8377dab | |||
| 5d4630519c | |||
| a7abf454d9 | |||
| 5a67ac8086 | |||
| 85cf8be705 | |||
| 2790a7d24b | |||
| 619ae393bd | |||
| 8240db5d22 | |||
| 05f46ec830 | |||
| 3b8a1ce7ee | |||
| a290b9495c | |||
| 8c9f1e0a55 | |||
| ffc5905309 | |||
| 3ed5540f40 | |||
| 7dc3412903 | |||
| 7c85a7835b | |||
| ecf48de7e8 | |||
| ecbca89cef | |||
| cf942d223e | |||
| adc1d6fc06 | |||
| 6624a0c535 | |||
| a65125a239 | |||
| 14b1eacf0d | |||
| d9e3531c49 | |||
| 5e5f616888 | |||
| 4222834539 | |||
| 2525ca2b32 | |||
| cfd3681979 | |||
| c2c2190cb6 | |||
| 6542f9b6b6 | |||
| 231ed083be | |||
| c1c33a0619 | |||
| 7e6ad8067c | |||
| 287347168d | |||
| f3428420e2 | |||
| 38267fa432 | |||
| 30405e768b | |||
| 3b57b14d9b | |||
| 367764b71e | |||
| acf7ee49aa | |||
| 751c012f3d | |||
| 7084806aac | |||
| b2fa98051f | |||
| 4f5b5c535a | |||
| e5ba94cb33 | |||
| 12e1785ff2 | |||
| bd1e77c2a3 | |||
| d2362a3f1e | |||
| 3a2f64d47d | |||
| d32186c101 | |||
| f7d428180b | |||
| c5d2caa345 | |||
| 89d614fa00 | |||
| a9e6272d2d | |||
| f2946db57a | |||
| 9cb56b9b04 | |||
| 6fa49d4113 | |||
| 90f155d334 | |||
| d47f79be1a | |||
| ec128f670a | |||
| a27089bc0c | |||
| c94a8dafa9 | |||
| 47840901c5 | |||
| e3e6c713d2 | |||
| 77016a03c7 | |||
| c1ae048c14 | |||
| 799117d287 | |||
| c445bfacad | |||
| 41871a1e8e | |||
| 444b9c0faf | |||
| 5547b427bb | |||
| 2dfc42821c | |||
| 33dcf9bb4a | |||
| a5e7b20699 | |||
| 93e74951a7 | |||
| 48721759d3 | |||
| 4f3b389f05 | |||
| 628226151b | |||
| 9a21c705b7 | |||
| 7888a7598b | |||
| c25369b766 | |||
| 6b5bfc853e | |||
| e691124058 | |||
| fdb9f96382 | |||
| c64f393a7b | |||
| ba0a630fd9 | |||
| 2468671d82 | |||
| 7242a989ba | |||
| d2150153df | |||
| 3ed7bc147b | |||
| 39d84a3db2 | |||
| 15df731e37 | |||
| bfa037885f | |||
| 8757537344 | |||
| 65aa669522 | |||
| f4e74b38d4 | |||
| c050c005e4 | |||
| af1fb6f61e | |||
| 09e5653191 | |||
| 29f6533e63 | |||
| 4821764a4f | |||
| 5dfa79d357 | |||
| 9eb21e3ab4 | |||
| af8644ebce | |||
| 0bde11b86e | |||
| d8be6d6d95 | |||
| 9731fcab48 | |||
| 0e4f6c4b01 | |||
| dd198349fb | |||
| 808bcadfca | |||
| ab37af59f2 | |||
| ce305eab58 | |||
| e590f541b3 | |||
| 48a9398905 | |||
| 3855bbd4be |
@@ -5,7 +5,6 @@
|
||||
.env.production
|
||||
node_modules
|
||||
.next
|
||||
src/generated
|
||||
*.md
|
||||
docker-compose.yml
|
||||
docker-compose.prod.yml
|
||||
|
||||
+33
-2
@@ -12,5 +12,36 @@ S3_ACCESS_KEY=""
|
||||
S3_SECRET_KEY=""
|
||||
S3_REGION="eu-central"
|
||||
|
||||
# Kinescope (добавить при получении платного плана)
|
||||
# KINESCOPE_API_KEY=""
|
||||
# Cloudflare Turnstile (anti-bot защита на регистрации)
|
||||
NEXT_PUBLIC_TURNSTILE_SITE_KEY=""
|
||||
TURNSTILE_SECRET_KEY=""
|
||||
|
||||
# Яндекс.Метрика — ID счётчика school.second-brain.ru (цели signup_free/gate_view).
|
||||
# Build-time (NEXT_PUBLIC_*) — передавать через --build-arg при сборке. Сам счётчик
|
||||
# (ym init snippet) ставится в admin → Settings → headCode, отдельно от этого ID.
|
||||
NEXT_PUBLIC_METRIKA_COUNTER_ID=""
|
||||
|
||||
# Kinescope API
|
||||
KINESCOPE_API_KEY=""
|
||||
|
||||
# Internal access-provisioning (payment-router → /api/internal/grant)
|
||||
INTERNAL_GRANT_SECRET=""
|
||||
|
||||
# Freemium: slug бесплатного курса, в который авто-записываем новых юзеров при регистрации
|
||||
FREE_COURSE_SLUG=obsidian-start
|
||||
|
||||
# Freemium quiz funnel (Typebot → /api/internal/quiz-lead)
|
||||
QUIZ_LEAD_SECRET=""
|
||||
# Listmonk API base — публичный домен (Listmonk живёт на Hetzner, наружу через newsletter.)
|
||||
LISTMONK_URL="https://newsletter.second-brain.ru"
|
||||
LISTMONK_USER="apiuser"
|
||||
LISTMONK_TOKEN=""
|
||||
LISTMONK_QUIZ_LIST_ID=""
|
||||
|
||||
# Freemium gate: ID wow-урока (2.6 «ежедневные заметки») — после его завершения
|
||||
# показываем лестницу-оффер (трипвайр + полный курс)
|
||||
WOW_MOMENT_LESSON_ID="obs-start-l2-006"
|
||||
|
||||
# Obsidian Toolbox (/tools): "true" — показывать точки входа и роуты (staging);
|
||||
# не задано/иное — скрыто, /tools редиректит на /dashboard (prod, пока дорабатываем)
|
||||
TOOLBOX_VISIBLE=""
|
||||
|
||||
@@ -45,3 +45,6 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
|
||||
/src/generated/prisma
|
||||
|
||||
# Claude Code local plugins (external git repos, не коммитим)
|
||||
.claude/plugins/
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Auto-generated Prisma client — раздувает граф 28 псевдо-сообществами
|
||||
src/generated/
|
||||
|
||||
# Superpowers / Claude Code инфраструктура, к LMS-коду не относится
|
||||
.claude/
|
||||
|
||||
# Build/cache артефакты (на всякий случай, помимо .gitignore)
|
||||
.next/
|
||||
out/
|
||||
build/
|
||||
coverage/
|
||||
@@ -0,0 +1,86 @@
|
||||
<h2>Как выглядит раздел «Вопросы» в админке?</h2>
|
||||
<p class="subtitle">Список + открытый тред — выбери что ближе по ощущению</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>Список + боковая панель треда</h3>
|
||||
<p>Слева список вопросов, справа открывается тред. Как почтовый клиент — не уходишь со страницы.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Admin → Вопросы учеников</div>
|
||||
<div class="mockup-body" style="padding:0; display:flex; min-height:160px; font-size:12px">
|
||||
<div style="width:45%; border-right:2px solid #AAAAAA; padding:8px">
|
||||
<div style="margin-bottom:6px; display:flex; gap:4px">
|
||||
<span style="padding:2px 8px; background:#323232; color:#F5F5F0; border-radius:2px; font-size:10px">Открытые 4</span>
|
||||
<span style="padding:2px 8px; background:#E8E8E0; border-radius:2px; font-size:10px">Закрытые</span>
|
||||
</div>
|
||||
<div style="padding:6px; background:#E8F0D8; border-left:3px solid #323232; margin-bottom:4px">
|
||||
<div style="font-weight:700">Алексей М.</div>
|
||||
<div style="color:#666; font-size:11px">Как экспортировать в PDF?</div>
|
||||
<div style="color:#999; font-size:10px">15 мин назад · 🔴 новое</div>
|
||||
</div>
|
||||
<div style="padding:6px; background:#F5F5F0; border-left:3px solid transparent; margin-bottom:4px; border:1px solid #E8E8E0">
|
||||
<div>Мария К.</div>
|
||||
<div style="color:#666; font-size:11px">Плагины не устанавливаются</div>
|
||||
<div style="color:#999; font-size:10px">2 часа назад</div>
|
||||
</div>
|
||||
<div style="padding:6px; background:#F5F5F0; border:1px solid #E8E8E0">
|
||||
<div>Иван С.</div>
|
||||
<div style="color:#666; font-size:11px">Синхронизация с телефоном</div>
|
||||
<div style="color:#999; font-size:10px">вчера · 🔴 новое</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex:1; padding:8px; display:flex; flex-direction:column">
|
||||
<div style="font-weight:700; margin-bottom:2px; font-size:13px">Как экспортировать в PDF?</div>
|
||||
<div style="color:#666; font-size:11px; margin-bottom:8px">Алексей М. · Obsidian. Полный курс</div>
|
||||
<div style="background:#E8E8E0; padding:6px; border-radius:2px; margin-bottom:6px; font-size:12px">
|
||||
Привет! Пытаюсь экспортировать заметки в PDF через Pandoc, но получаю ошибку...
|
||||
</div>
|
||||
<div style="margin-top:auto; display:flex; gap:4px">
|
||||
<input style="flex:1; padding:4px 6px; border:1px solid #AAAAAA; font-size:11px; border-radius:2px" placeholder="Ответить...">
|
||||
<button style="padding:4px 8px; background:#323232; color:#F5F5F0; border:none; font-size:11px; border-radius:2px">→</button>
|
||||
<button style="padding:4px 8px; background:#E8E8E0; border:1px solid #AAAAAA; font-size:10px; border-radius:2px">✓ Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>Список → отдельная страница треда</h3>
|
||||
<p>Список всех вопросов, клик открывает отдельную страницу. Проще в реализации, больше места для переписки.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Admin → Вопросы / #42 — Алексей М.</div>
|
||||
<div class="mockup-body" style="padding:8px; font-size:12px">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px">
|
||||
<span style="font-weight:700">Как экспортировать в PDF?</span>
|
||||
<span style="padding:2px 8px; background:#E8F0D8; border:1px solid #AAAAAA; font-size:10px; border-radius:2px">● ОТКРЫТ</span>
|
||||
</div>
|
||||
<div style="display:flex; flex-direction:column; gap:5px; margin-bottom:8px">
|
||||
<div style="background:#E8E8E0; padding:5px 8px; border-radius:2px; max-width:80%">
|
||||
<div style="font-size:10px; color:#666">Алексей · 14:32</div>
|
||||
Пытаюсь экспортировать через Pandoc, получаю ошибку...
|
||||
</div>
|
||||
<div style="background:#F5F5F0; border:2px solid #E8F0D8; padding:5px 8px; border-radius:2px; max-width:80%; align-self:flex-end">
|
||||
<div style="font-size:10px; color:#666">Дмитрий (admin) · 14:55</div>
|
||||
Покажи текст ошибки?
|
||||
</div>
|
||||
<div style="background:#E8E8E0; padding:5px 8px; border-radius:2px; max-width:80%">
|
||||
<div style="font-size:10px; color:#666">Алексей · 15:01</div>
|
||||
"Error: pandoc not found in PATH"
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex; gap:4px">
|
||||
<input style="flex:1; padding:4px 6px; border:1px solid #AAAAAA; font-size:11px; border-radius:2px" placeholder="Написать ответ...">
|
||||
<button style="padding:4px 10px; background:#323232; color:#F5F5F0; border:none; font-size:11px; border-radius:2px">Отправить</button>
|
||||
<button style="padding:4px 8px; background:#E8E8E0; border:1px solid #AAAAAA; font-size:10px; border-radius:2px">✓ Закрыть вопрос</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,56 @@
|
||||
<h2>Как устроен диалог после отправки вопроса?</h2>
|
||||
<p class="subtitle">Влияет на структуру БД и сложность реализации</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>Один вопрос → один ответ</h3>
|
||||
<p>Студент задаёт вопрос. Админ отвечает один раз. Закрыто. Просто и быстро в реализации.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Страница вопроса у студента</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
<div style="background:#E8E8E0; padding:8px; border-radius:2px; margin-bottom:8px">
|
||||
<div style="font-size:11px; color:#666; margin-bottom:4px">Ты · 14 мая</div>
|
||||
<div>Как правильно настроить шаблоны в Obsidian?</div>
|
||||
</div>
|
||||
<div style="background:#E8F0D8; border:2px solid #323232; padding:8px; border-radius:2px">
|
||||
<div style="font-size:11px; color:#666; margin-bottom:4px">Дмитрий · 15 мая ✅ Отвечено</div>
|
||||
<div>Зайди в Настройки → Шаблоны → укажи папку...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>Тред (переписка)</h3>
|
||||
<p>Студент и админ могут писать несколько сообщений. Как поддержка-чат внутри платформы.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Тред вопроса</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
<div style="background:#E8E8E0; padding:6px 8px; border-radius:2px; margin-bottom:6px; margin-left:0; max-width:85%">
|
||||
<div style="font-size:10px; color:#666">Ты · 14 мая</div>
|
||||
Как настроить шаблоны?
|
||||
</div>
|
||||
<div style="background:#E8F0D8; padding:6px 8px; border-radius:2px; margin-bottom:6px; margin-left:auto; max-width:85%; text-align:right">
|
||||
<div style="font-size:10px; color:#666">Дмитрий · 15 мая</div>
|
||||
Зайди в Настройки → Шаблоны...
|
||||
</div>
|
||||
<div style="background:#E8E8E0; padding:6px 8px; border-radius:2px; margin-bottom:6px; max-width:85%">
|
||||
<div style="font-size:10px; color:#666">Ты · 15 мая</div>
|
||||
А какую папку лучше назвать?
|
||||
</div>
|
||||
<div style="border:1px solid #AAAAAA; border-radius:2px; padding:6px 8px; color:#999; font-size:12px">Написать сообщение...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section" style="margin-top:16px; padding:12px; background:#F5F5F0; border:1px solid #AAAAAA; border-radius:2px">
|
||||
<span class="label">Рекомендация</span>
|
||||
<p style="margin:4px 0 0; font-size:14px">Вариант А проще, закрывает 90% случаев. Тред (Б) полезен когда ответ требует уточнений, но добавляет ~40% сложности реализации.</p>
|
||||
</div>
|
||||
@@ -0,0 +1,56 @@
|
||||
<h2>Где студент начинает «Задать вопрос»?</h2>
|
||||
<p class="subtitle">Влияет на то, будет ли вопрос привязан к уроку или нет</p>
|
||||
|
||||
<div class="options">
|
||||
<div class="option" data-choice="a" onclick="toggleSelect(this)">
|
||||
<div class="letter">A</div>
|
||||
<div class="content">
|
||||
<h3>Только главное меню</h3>
|
||||
<p>Пункт «Задать вопрос» в хедере / sidebar студента. Общий вопрос без привязки к уроку.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Студент — sidebar</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
<div style="padding:8px; margin-bottom:4px; background:#E8E8E0; border-radius:2px">📚 Мои курсы</div>
|
||||
<div style="padding:8px; margin-bottom:4px; background:#E8E8E0; border-radius:2px">👤 Профиль</div>
|
||||
<div style="padding:8px; background:#E8F0D8; border:2px solid #323232; border-radius:2px; font-weight:700">❓ Задать вопрос</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="b" onclick="toggleSelect(this)">
|
||||
<div class="letter">B</div>
|
||||
<div class="content">
|
||||
<h3>Только со страницы урока</h3>
|
||||
<p>Кнопка на странице урока рядом с комментариями. Вопрос автоматически привязан к курсу и уроку.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Страница урока — низ</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
<div style="margin-bottom:8px; color:#666">💬 Комментарии (3)</div>
|
||||
<div style="display:flex; gap:8px">
|
||||
<div style="padding:6px 12px; background:#E8E8E0; border:1px solid #AAAAAA; border-radius:2px; font-size:12px">Написать комментарий</div>
|
||||
<div style="padding:6px 12px; background:#F5F5F0; border:2px solid #323232; border-radius:2px; font-size:12px; font-weight:700">❓ Задать приватный вопрос</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option" data-choice="c" onclick="toggleSelect(this)">
|
||||
<div class="letter">C</div>
|
||||
<div class="content">
|
||||
<h3>Оба места</h3>
|
||||
<p>Пункт в меню для общих вопросов + кнопка на уроке для вопросов по конкретному уроку. Контекст подставляется автоматически.</p>
|
||||
<div class="mockup" style="margin-top:12px">
|
||||
<div class="mockup-header">Два источника → одна очередь у админа</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
<div style="display:flex; gap:8px; margin-bottom:8px">
|
||||
<div style="padding:6px 10px; background:#E8F0D8; border:2px solid #323232; font-size:11px; font-weight:700; border-radius:2px">Меню → общий вопрос</div>
|
||||
<div style="padding:6px 10px; background:#E8F0D8; border:2px solid #323232; font-size:11px; font-weight:700; border-radius:2px">Урок → вопрос по уроку</div>
|
||||
</div>
|
||||
<div style="padding:8px; border:1px solid #AAAAAA; border-radius:2px; color:#666; font-size:12px">→ Один список у админа: «Вопросы учеников»</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,108 @@
|
||||
<h2>Студент — раздел «Мои вопросы»</h2>
|
||||
<p class="subtitle">Список + открытый тред. Скажи что поправить.</p>
|
||||
|
||||
<div class="split" style="gap:16px; align-items:flex-start">
|
||||
|
||||
<!-- Список вопросов -->
|
||||
<div class="mockup" style="flex:1">
|
||||
<div class="mockup-header">/questions — Мои вопросы</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:12px">
|
||||
<span style="font-weight:700; font-size:15px">Мои вопросы</span>
|
||||
<button style="padding:6px 14px; background:#F5F5F0; border:2px solid #323232; font-size:12px; font-weight:700; border-radius:2px; cursor:pointer">+ Задать вопрос</button>
|
||||
</div>
|
||||
|
||||
<!-- Открытый вопрос с новым ответом -->
|
||||
<div style="border:2px solid #323232; border-radius:2px; padding:10px; margin-bottom:8px; background:#F5F5F0; cursor:pointer">
|
||||
<div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:4px">
|
||||
<span style="font-weight:700; font-size:13px">Как настроить синхронизацию с телефоном?</span>
|
||||
<span style="padding:2px 7px; background:#E8F0D8; border:1px solid #AAAAAA; font-size:10px; border-radius:2px; white-space:nowrap; margin-left:8px">● ОТКРЫТ</span>
|
||||
</div>
|
||||
<div style="color:#666; font-size:11px; margin-bottom:6px">3 сообщения · последнее от Дмитрия</div>
|
||||
<div style="display:flex; align-items:center; gap:6px">
|
||||
<span style="display:inline-block; width:8px; height:8px; background:#323232; border-radius:50%"></span>
|
||||
<span style="font-size:11px; font-weight:700">Новый ответ от школы</span>
|
||||
<span style="color:#999; font-size:11px">· 20 мин назад</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Обычный открытый вопрос -->
|
||||
<div style="border:1px solid #AAAAAA; border-radius:2px; padding:10px; margin-bottom:8px; background:#F5F5F0; cursor:pointer">
|
||||
<div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:4px">
|
||||
<span style="font-size:13px">Ошибка при установке плагина Dataview</span>
|
||||
<span style="padding:2px 7px; background:#E8F0D8; border:1px solid #AAAAAA; font-size:10px; border-radius:2px; white-space:nowrap; margin-left:8px">● ОТКРЫТ</span>
|
||||
</div>
|
||||
<div style="color:#666; font-size:11px">2 сообщения · ожидает ответа школы</div>
|
||||
</div>
|
||||
|
||||
<!-- Закрытый вопрос -->
|
||||
<div style="border:1px solid #E8E8E0; border-radius:2px; padding:10px; background:#FAFAFA; cursor:pointer; opacity:0.7">
|
||||
<div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:4px">
|
||||
<span style="font-size:13px; color:#666">Как экспортировать заметки в PDF?</span>
|
||||
<span style="padding:2px 7px; background:#E8E8E0; font-size:10px; border-radius:2px; white-space:nowrap; margin-left:8px; color:#666">✓ ЗАКРЫТ</span>
|
||||
</div>
|
||||
<div style="color:#999; font-size:11px">5 сообщений · закрыт 10 мая</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Страница треда -->
|
||||
<div class="mockup" style="flex:1.2">
|
||||
<div class="mockup-header">/questions/[id] — Открытый тред</div>
|
||||
<div class="mockup-body" style="padding:12px; font-size:13px">
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px">
|
||||
<div>
|
||||
<div style="font-weight:700; font-size:14px">Как настроить синхронизацию с телефоном?</div>
|
||||
<div style="font-size:11px; color:#666">Создан 18 мая · <span style="color:#323232; font-weight:700">● Открыт</span></div>
|
||||
</div>
|
||||
<a href="#" style="font-size:11px; color:#666; text-decoration:underline">← Все вопросы</a>
|
||||
</div>
|
||||
|
||||
<!-- Сообщения треда -->
|
||||
<div style="display:flex; flex-direction:column; gap:8px; margin-bottom:12px; max-height:200px; overflow-y:auto">
|
||||
|
||||
<div style="background:#E8E8E0; padding:8px 10px; border-radius:2px; max-width:88%">
|
||||
<div style="font-size:10px; color:#666; margin-bottom:3px">Ты · 18 мая, 10:14</div>
|
||||
<div>Привет! Хочу синхронизировать хранилище между Mac и iPhone. Пробовал iCloud, но заметки иногда конфликтуют. Есть ли рекомендованный способ?</div>
|
||||
</div>
|
||||
|
||||
<div style="background:#F5F5F0; border:2px solid #E8F0D8; padding:8px 10px; border-radius:2px; max-width:88%; align-self:flex-end">
|
||||
<div style="font-size:10px; color:#666; margin-bottom:3px">Дмитрий (куратор) · 18 мая, 11:30</div>
|
||||
<div>Рекомендую Obsidian Sync — он создан специально для этого. Или как альтернатива — Working Copy + Git. Какой вариант тебе ближе?</div>
|
||||
</div>
|
||||
|
||||
<div style="background:#E8E8E0; padding:8px 10px; border-radius:2px; max-width:88%">
|
||||
<div style="font-size:10px; color:#666; margin-bottom:3px">Ты · 18 мая, 11:45</div>
|
||||
<div>Лучше без платной подписки если можно. Расскажи про Working Copy?</div>
|
||||
<!-- файл -->
|
||||
<div style="margin-top:6px; display:flex; align-items:center; gap:5px; padding:4px 7px; background:#F5F5F0; border:1px solid #AAAAAA; border-radius:2px; font-size:11px; width:fit-content">
|
||||
<span>📎</span> <span>screenshot.png</span> <span style="color:#999">· 84 KB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background:#F5F5F0; border:2px solid #E8F0D8; padding:8px 10px; border-radius:2px; max-width:88%; align-self:flex-end; border-left:3px solid #323232">
|
||||
<div style="font-size:10px; color:#666; margin-bottom:3px">Дмитрий (куратор) · 18 мая, 20 мин назад · <strong>🔵 новое</strong></div>
|
||||
<div>Working Copy — это Git-клиент для iOS. Схема: хранилище в приватном репо GitHub, Working Copy на телефоне синкает его автоматически...</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Форма ответа -->
|
||||
<div style="border:2px solid #AAAAAA; border-radius:2px; padding:8px; background:#F5F5F0">
|
||||
<textarea style="width:100%; border:none; background:transparent; font-size:13px; resize:none; outline:none; font-family:inherit; min-height:50px" placeholder="Написать сообщение..."></textarea>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-top:6px; padding-top:6px; border-top:1px solid #E8E8E0">
|
||||
<div style="display:flex; align-items:center; gap:6px">
|
||||
<button style="padding:4px 8px; background:#F5F5F0; border:1px solid #AAAAAA; font-size:11px; border-radius:2px; cursor:pointer">📎 Прикрепить</button>
|
||||
<span style="font-size:10px; color:#999">jpg, png, pdf, md · до 10 МБ</span>
|
||||
</div>
|
||||
<button style="padding:6px 16px; background:#323232; color:#F5F5F0; border:none; font-size:12px; font-weight:700; border-radius:2px; cursor:pointer">Отправить →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
|
||||
<p class="subtitle">Continuing in terminal...</p>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
|
||||
<p class="subtitle">Presenting design in terminal...</p>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
{"reason":"idle timeout","timestamp":1779178878212}
|
||||
@@ -0,0 +1,10 @@
|
||||
{"type":"server-started","port":49771,"host":"127.0.0.1","url_host":"localhost","url":"http://localhost:49771","screen_dir":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content","state_dir":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/state"}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/entry-point.html"}
|
||||
{"source":"user-event","type":"click","text":"A\n \n Только главное меню\n Пункт «Задать вопрос» в хедере / sidebar студента. Общий вопрос без привязки к уроку.\n \n Студент — sidebar\n \n 📚 Мои курсы\n 👤 Профиль\n ❓ Задать вопрос","choice":"a","id":null,"timestamp":1779175897914}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/conversation-model.html"}
|
||||
{"source":"user-event","type":"click","text":"B\n \n Тред (переписка)\n Студент и админ могут писать несколько сообщений. Как поддержка-чат внутри платформы.\n \n Тред вопроса\n \n \n Ты · 14 мая\n Как настроить шаблоны?\n \n \n Дмитрий · 15 мая\n Зайди в Настройки → Шаблоны...\n \n \n Ты · 15 мая\n А какую папку лучше назвать?\n \n Написать сообщение...","choice":"b","id":null,"timestamp":1779176002499}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/waiting-1.html"}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/admin-views.html"}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/waiting-2.html"}
|
||||
{"type":"screen-added","file":"/Users/dementiy/Documents/Claude/lms-system/.superpowers/brainstorm/8559-1779175817/content/student-questions.html"}
|
||||
{"type":"server-stopped","reason":"idle timeout"}
|
||||
@@ -0,0 +1 @@
|
||||
8568
|
||||
@@ -0,0 +1,62 @@
|
||||
# Quiz-Lead Task 3 — Implementation Report
|
||||
|
||||
Date: 20260622
|
||||
|
||||
## STATUS: DONE
|
||||
|
||||
## Files created/modified
|
||||
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `src/app/api/internal/quiz-lead/route.ts` | CREATED |
|
||||
| `.env.example` | MODIFIED — added QUIZ_LEAD_SECRET, LISTMONK_URL, LISTMONK_USER, LISTMONK_TOKEN, LISTMONK_QUIZ_LIST_ID |
|
||||
| `src/middleware.ts` | NOT MODIFIED — `/api/internal` already in PUBLIC_ROUTES with startsWith match; covers /api/internal/quiz-lead without changes |
|
||||
|
||||
## Test infra
|
||||
|
||||
`grep -E 'vitest|jest' package.json` → no output (empty).
|
||||
No test framework present. Verification done via `npx tsc --noEmit` + manual reasoning.
|
||||
|
||||
## Commands and output
|
||||
|
||||
### `npm run type-check`
|
||||
```
|
||||
> lms-sb@0.1.0 type-check
|
||||
> tsc --noEmit
|
||||
|
||||
(no output = clean)
|
||||
```
|
||||
Exit code: 0
|
||||
|
||||
### `npm run lint` (new file only)
|
||||
```
|
||||
grep 'quiz-lead' in lint output → no results (no errors in new file)
|
||||
```
|
||||
|
||||
Pre-existing lint errors in repo (36 total, 20 errors) are NOT related to this task:
|
||||
- `.claude/plugins/superpowers/` scripts using CommonJS require
|
||||
- `quick-enroll-modal.tsx` — react-hooks/immutability (pre-existing)
|
||||
- `kinescope-player.tsx` — react-hooks/set-state-in-effect (pre-existing)
|
||||
|
||||
## Commit
|
||||
|
||||
Base: `9fbb7ae`
|
||||
New: `5e65ad6`
|
||||
|
||||
```
|
||||
5e65ad6 Add quiz-lead internal endpoint for Typebot → Listmonk funnel
|
||||
```
|
||||
|
||||
## Implementation notes
|
||||
|
||||
- Auth: `x-quiz-secret` header vs `QUIZ_LEAD_SECRET` env var; 401 on mismatch/missing
|
||||
- Validation: email regex, archetype against const tuple, Number.isFinite(score); 400 on failure
|
||||
- Upsert logic: POST create → 409 → GET by email SQL query → PUT update; 502 on Listmonk errors
|
||||
- Middleware: `/api/internal` already in PUBLIC_ROUTES with `startsWith` — no changes needed
|
||||
- TypeScript strict mode: no `any` used; typed interfaces for Listmonk response shapes
|
||||
- No production Listmonk list created; LISTMONK_QUIZ_LIST_ID left as empty placeholder in .env.example
|
||||
- No git push; no deploy
|
||||
|
||||
## Concerns
|
||||
|
||||
None. The pre-existing lint errors in the repo are unrelated to this task and were already present before this commit.
|
||||
@@ -1,5 +1,308 @@
|
||||
<!-- BEGIN:nextjs-agent-rules -->
|
||||
# This is NOT the Next.js you know
|
||||
# AGENTS.md — LMS Second Brain
|
||||
|
||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
Собственная LMS-платформа для образовательных курсов по PKM и Obsidian.
|
||||
Заменяет emdesell.ru. Масштаб: ~1000 аккаунтов, ~200 активных, до 10 курсов.
|
||||
Production: **https://school.second-brain.ru**
|
||||
|
||||
> Подробная техническая документация — в `TECHNICAL.md`.
|
||||
> Роадмап и текущий статус — в `ROADMAP.md`.
|
||||
> Полные правила для Claude Code — в `CLAUDE.md`.
|
||||
|
||||
---
|
||||
|
||||
## Стек
|
||||
|
||||
| Слой | Технология | Версия |
|
||||
|------|-----------|--------|
|
||||
| Фреймворк | Next.js (App Router) | **16.2.2** |
|
||||
| Язык | TypeScript (strict) | 5.x |
|
||||
| UI | React | 19 |
|
||||
| Стили | Tailwind CSS (CSS-based, **без** tailwind.config.ts) | 4.x |
|
||||
| Компоненты | shadcn/ui (Base UI, **не Radix**) | v4 |
|
||||
| ORM | Prisma | 7.x |
|
||||
| Auth | Better Auth (**не NextAuth**) | 1.6.0 |
|
||||
| Редактор | TipTap WYSIWYG | 2.x |
|
||||
| Drag-and-drop | @dnd-kit | latest |
|
||||
| БД | PostgreSQL | 16 |
|
||||
| Email | Resend | latest |
|
||||
| Хранилище | Hetzner Object Storage (S3-совместимый) | — |
|
||||
| Видео | Kinescope (iframe embed) | — |
|
||||
| Валидация | Zod | 3.x |
|
||||
|
||||
---
|
||||
|
||||
## Критические отличия от стандартных версий
|
||||
|
||||
Эти технологии отличаются от того, что содержится в обучающих данных большинства моделей. **Читай документацию перед написанием кода.**
|
||||
|
||||
### Next.js 16.2.2
|
||||
- Используется `proxy.ts` вместо `middleware.ts`
|
||||
- Экспортируемая функция называется `proxy`, не `middleware`
|
||||
- Перед написанием кода смотри `node_modules/next/dist/docs/`
|
||||
|
||||
### Tailwind CSS v4
|
||||
- **Нет файла `tailwind.config.ts`** — вся кастомизация через CSS
|
||||
- Конфиг: `@import "tailwindcss"` и `@theme` в `globals.css`
|
||||
|
||||
### shadcn/ui v4
|
||||
- Базируется на `@base-ui/react`, **не Radix**
|
||||
- Нет пропа `asChild` — триггеры обычные элементы
|
||||
- Установка: `npx shadcn@latest add <component>`
|
||||
|
||||
### Prisma 7.x
|
||||
- Импорт: `from "@/generated/prisma/client"` (не `from "@/generated/prisma"`)
|
||||
- Требует адаптер: `new PrismaPg({ connectionString })`
|
||||
- Не генерирует `index.ts`
|
||||
|
||||
### Better Auth 1.6.0
|
||||
- **Не путать с NextAuth** — другая библиотека, другое API
|
||||
- В этом проекте используется **bcrypt** (не scrypt по умолчанию)
|
||||
- Настройки `password.hash` / `password.verify` в `src/lib/auth.ts`
|
||||
- `auth-client.ts` не использует `baseURL` — берёт `window.location.origin`
|
||||
- Seed-пользователи вставлены через SQL с `emailVerified = true`
|
||||
|
||||
---
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
# Разработка
|
||||
npm run dev # localhost:3000
|
||||
docker compose up -d # Поднять PostgreSQL локально
|
||||
|
||||
# Проверка качества
|
||||
npm run lint # ESLint
|
||||
npm run type-check # tsc --noEmit
|
||||
|
||||
# Сборка
|
||||
npm run build
|
||||
npm run start
|
||||
|
||||
# База данных
|
||||
npx prisma migrate dev --name <snake_case_name> # Новая миграция
|
||||
npx prisma migrate deploy # Применить в production
|
||||
npx prisma generate # Пересоздать клиент
|
||||
npx prisma db seed # Заполнить тестовыми данными
|
||||
npx prisma studio # GUI для БД
|
||||
|
||||
# Production-деплой (на сервере в /root/digital-household/lms-sb/)
|
||||
git pull
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
```
|
||||
|
||||
При старте production-контейнер автоматически запускает `prisma migrate deploy`, затем `node server.js`.
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
lms-system/
|
||||
├── src/
|
||||
│ ├── app/ # Next.js App Router
|
||||
│ │ ├── (auth)/ # login, register, verify-email
|
||||
│ │ ├── (student)/ # dashboard, courses/[slug], lessons/[lessonId]
|
||||
│ │ ├── curator/ # homework review, dashboard
|
||||
│ │ ├── admin/ # courses, users, settings, categories
|
||||
│ │ └── api/ # REST endpoints + Better Auth handler
|
||||
│ ├── components/
|
||||
│ │ ├── ui/ # shadcn/ui (автогенерация, не трогать)
|
||||
│ │ ├── editor/ # TipTap WYSIWYG
|
||||
│ │ ├── player/ # Kinescope Player wrapper
|
||||
│ │ ├── course/ # Компоненты курса
|
||||
│ │ └── layout/ # Header, Sidebar, Footer
|
||||
│ ├── lib/
|
||||
│ │ ├── auth.ts # Better Auth config (сервер)
|
||||
│ │ ├── auth-client.ts # Better Auth client (браузер)
|
||||
│ │ ├── prisma.ts # Prisma singleton
|
||||
│ │ ├── s3.ts # Hetzner S3 клиент
|
||||
│ │ ├── email.ts # Resend email helpers
|
||||
│ │ └── utils.ts # cn() и утилиты
|
||||
│ ├── types/ # TypeScript-типы
|
||||
│ ├── proxy.ts # Auth middleware (защита маршрутов)
|
||||
│ └── middleware.ts # Обёртка над proxy
|
||||
├── prisma/
|
||||
│ ├── schema.prisma # Схема БД (~314 строк)
|
||||
│ ├── seed.ts # Тестовые данные
|
||||
│ └── migrations/ # НЕ РЕДАКТИРОВАТЬ ВРУЧНУЮ
|
||||
├── docker-compose.yml # Локальная разработка
|
||||
├── docker-compose.prod.yml # Production
|
||||
├── Dockerfile # Multi-stage build
|
||||
├── .env.example # Шаблон переменных (без секретов)
|
||||
└── .env.local # Локальные секреты (в .gitignore)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Роли и маршруты
|
||||
|
||||
| Роль | Маршруты | Описание |
|
||||
|------|---------|----------|
|
||||
| `admin` | `/admin/*`, `/curator/*`, всё | Полный доступ |
|
||||
| `curator` | `/curator/*`, `/dashboard` | Проверка ДЗ, комментарии |
|
||||
| `student` | `/dashboard`, `/courses/*` | Просмотр курсов, прогресс |
|
||||
|
||||
Защита маршрутов — в `src/proxy.ts` + проверка сессии в layout/page.
|
||||
|
||||
---
|
||||
|
||||
## Модель данных (ключевые сущности)
|
||||
|
||||
```
|
||||
User → Session, Account, Verification # Better Auth
|
||||
Category → Course → Module → Lesson # Структура контента
|
||||
Lesson → LessonFile # Файлы к уроку
|
||||
CourseEnrollment (userId + courseId) # Доступ с expiresAt
|
||||
AccessLog # Аудит доступов
|
||||
LessonProgress (userId + lessonId) # Прогресс ученика
|
||||
Lesson → Homework → HomeworkSubmission → HomeworkFeedback # ДЗ
|
||||
Lesson → LessonComment # Обсуждения (soft-delete)
|
||||
Lesson → Quiz → QuizQuestion → QuizOption # Тесты
|
||||
Quiz → QuizAttempt # Результаты тестов
|
||||
Settings (key-value) # Настройки платформы
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Дизайн-система «Second Brain Aubade»
|
||||
|
||||
Типографский, монохромный, газетный стиль.
|
||||
|
||||
| Токен | Значение |
|
||||
|-------|---------|
|
||||
| Шрифт | Fira Mono (400/500/700, Latin + Cyrillic) |
|
||||
| Фон | `#F5F5F0` (тёплый off-white) |
|
||||
| Текст | `#323232` |
|
||||
| Поверхность | `#E8E8E0` |
|
||||
| Акцент | `#E8F0D8` (зелёный) |
|
||||
| Border | `#AAAAAA` |
|
||||
| Сайдбар | `#2A2A28` (тёмный) |
|
||||
|
||||
**Aubade-эффект** (карточки и кнопки):
|
||||
- Border: `2px solid #AAAAAA`
|
||||
- Shadow: `4px 4px 0 0 #AAAAAA`
|
||||
- Hover: `translate(-2px, -2px)` + shadow `6px 6px`
|
||||
- Active: `translate(2px, 2px)`, shadow убирается
|
||||
- CSS-классы: `.card-aubade`, `.btn-aubade`, `.btn-aubade-accent`, `.tag-aubade`
|
||||
|
||||
---
|
||||
|
||||
## Инфраструктура
|
||||
|
||||
| Компонент | Значение |
|
||||
|-----------|---------|
|
||||
| Сервер | Hetzner VPS — 8 vCPU / 16 GB RAM / 320 GB NVMe |
|
||||
| Reverse proxy | Caddy (auto HTTPS, Let's Encrypt) |
|
||||
| Порт | 3010 (внутри контейнера 3000) |
|
||||
| БД | PostgreSQL 16 (контейнер `lms-sb-db-1`) |
|
||||
| Object Storage | Hetzner S3, endpoint `nbg1.your-objectstorage.com`, бакет `second-brain-lms` |
|
||||
| Git | Gitea — `https://git.second-brain.ru/admins/lms-sb` |
|
||||
| Email | Resend, домен `mailsend.second-brain.ru` |
|
||||
| Бэкапы | PostgreSQL → Backblaze B2 (ежедневно, 03:00, ротация 7 дней) |
|
||||
|
||||
---
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
```env
|
||||
DATABASE_URL="postgresql://lms_user:password@localhost:5432/lms_db"
|
||||
BETTER_AUTH_SECRET="generate-with-openssl-rand-base64-32"
|
||||
BETTER_AUTH_URL="http://localhost:3000"
|
||||
RESEND_API_KEY=""
|
||||
EMAIL_FROM="noreply@mailsend.second-brain.ru"
|
||||
S3_ENDPOINT="https://nbg1.your-objectstorage.com"
|
||||
S3_BUCKET="second-brain-lms"
|
||||
S3_ACCESS_KEY=""
|
||||
S3_SECRET_KEY=""
|
||||
S3_REGION="eu-central"
|
||||
```
|
||||
|
||||
Секреты — **только в `.env.local`**. При добавлении новых переменных обновлять `.env.example`.
|
||||
|
||||
---
|
||||
|
||||
## Правила написания кода
|
||||
|
||||
### Языки
|
||||
- **UI-строки** (заголовки, кнопки, сообщения): русский
|
||||
- **Переменные, функции, файлы, комментарии**: английский
|
||||
- **Коммиты**: английский, imperative mood (`Add lesson progress`, `Fix auth redirect`)
|
||||
|
||||
### Стиль
|
||||
- Server Actions для форм и мутаций
|
||||
- Не добавлять абстракции «на будущее» — только текущий этап
|
||||
- Нет `console.log` в production (только `console.error` для реальных ошибок)
|
||||
- Нет захардкоженных секретов, URL, ID
|
||||
|
||||
### Миграции БД
|
||||
- **Никогда** не редактировать `prisma/migrations/` вручную
|
||||
- **Всегда** спрашивать перед миграцией, которая меняет или удаляет существующие поля
|
||||
- Имена миграций: английский, snake_case (`add_lesson_progress`)
|
||||
- Перед `prisma migrate deploy` на production — бэкап БД
|
||||
|
||||
### Файлы и загрузки
|
||||
- Все файлы (ДЗ, PDF, изображения) — через Hetzner Object Storage, **не на диск VPS**
|
||||
- Обложки курсов: 16:9, max 5 MB, JPG/PNG/WebP
|
||||
- Изображения в уроках: max 10 MB
|
||||
- Файлы к уроку: max 100 MB, PDF/ZIP/DOCX
|
||||
|
||||
### Коммиты
|
||||
- Один коммит = одна логически завершённая единица
|
||||
- Перед коммитом: `npm run lint && npm run type-check`
|
||||
- После завершения каждого этапа ROADMAP — `git push` в Gitea
|
||||
|
||||
---
|
||||
|
||||
## Чек-лист перед коммитом
|
||||
|
||||
- [ ] `npm run lint` — без ошибок
|
||||
- [ ] `npm run type-check` — без ошибок
|
||||
- [ ] Новые `.env` переменные добавлены в `.env.example`
|
||||
- [ ] Миграция БД согласована (если есть)
|
||||
- [ ] Нет `console.log`, нет секретов в коде
|
||||
|
||||
---
|
||||
|
||||
## Тестовые аккаунты
|
||||
|
||||
| Email | Пароль | Роль |
|
||||
|-------|--------|------|
|
||||
| admin@second-brain.ru | Password123! | admin |
|
||||
| curator@second-brain.ru | Password123! | curator |
|
||||
| student@second-brain.ru | Password123! | student |
|
||||
|
||||
---
|
||||
|
||||
## Текущий статус проекта
|
||||
|
||||
**Завершено (9 из 13 этапов):**
|
||||
- Этап 0: Каркас, auth, роли, деплой
|
||||
- Этап 1: Курсы → Модули → Уроки (CRUD, drag-and-drop, TipTap, S3)
|
||||
- Этап 1.5: Расширенный доступ (сроки, категории, AccessLog)
|
||||
- Этап 2: Kinescope-интеграция, рендер уроков для ученика
|
||||
- Этап 3: Прогресс (кнопка завершения, прогресс-бар)
|
||||
- Этап 5: Домашние задания + обратная связь куратора
|
||||
- Этап 6: Обсуждения под уроками
|
||||
- Этап 7: Email-уведомления (Resend)
|
||||
- Этап 8: Импорт уроков из Markdown (Obsidian)
|
||||
|
||||
**В работе:**
|
||||
- Этап 9: Настройки платформы (Admin Settings)
|
||||
|
||||
**Впереди:**
|
||||
- Этап 11: Импорт/экспорт учеников (CSV, миграция с emdesell)
|
||||
- Этап 12: Telegram-бот + аналитика (Yandex.Metrika)
|
||||
- Этап 13: Тесты и квизы с автопроверкой
|
||||
|
||||
**Бэклог:** сертификаты, геймификация, платежи, медиатека, цифровой сад, CI/CD
|
||||
|
||||
Полный роадмап с деталями и критериями готовности — в `ROADMAP.md`.
|
||||
|
||||
---
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Seed-пользователи вставлены через SQL с `emailVerified = true` (обход Better Auth)
|
||||
- Загрузка файлов: нет лимита на уровне Next.js (только S3)
|
||||
- Drag-and-drop: возможны race conditions при быстрых перетаскиваниях (некритично)
|
||||
- `expiresAt` проверяется в UI, но не блокирует доступ на уровне middleware
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| TypeScript | 5.x | Язык |
|
||||
| React | 19 | UI |
|
||||
| PostgreSQL | 16 | База данных |
|
||||
| Prisma | 6.x | ORM + миграции |
|
||||
| Prisma | 7.x | ORM + миграции |
|
||||
| Better Auth | latest | Аутентификация и сессии |
|
||||
| Tailwind CSS | 4.x (CSS-based config) | Стили (нет tailwind.config.ts) |
|
||||
| shadcn/ui | latest | UI-компоненты |
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Дизайн-система — LMS
|
||||
|
||||
Этот проект использует дизайн-систему **ДС-2 «Second Brain LMS & Press»** — терминальный язык «Aubade».
|
||||
|
||||
## Канон
|
||||
|
||||
Полная спецификация (9 секций, формат `DESIGN.md`):
|
||||
|
||||
- **Source of truth:** `SecondBrainTech/02-Стандарты/Дизайн-LMS/DESIGN.md`
|
||||
- Копия для Open Design: `~/Documents/Claude/open-design/design-systems/second-brain-lms/`
|
||||
- Превью со всеми примерами: `preview.html` в тех же каталогах
|
||||
|
||||
> Канон правится только там. Этот файл — практический указатель для разработки внутри репозитория.
|
||||
|
||||
## Язык в двух словах
|
||||
|
||||
Терминальный, моноширинный, «реестровый». Серо-зелёная палитра, острые углы 2px, выраженные рамки 2px, жёсткие тени-подложки с физикой hover/active. Тёмный админ-сайдбар. Без кремовых тонов и серифа — это язык ДС-1 (сайт и PDF), отдельной парной системы.
|
||||
|
||||
## Где токены в этом репозитории
|
||||
|
||||
Реализация — `src/app/globals.css`:
|
||||
|
||||
- **Палитра** — CSS-переменные в `:root`: `--background #F5F5F0`, `--foreground #323232`, `--accent #E8F0D8`, `--border #AAAAAA`, тёмный сайдбар `--sidebar-*`.
|
||||
- **Типографическая шкала** — переопределённые токены Tailwind `--text-*` в блоке `@theme` (канон ДС-2, +2px к дефолту Tailwind).
|
||||
- **Шрифт** — Fira Mono, подключение в `src/app/layout.tsx` через `next/font/google`.
|
||||
- **Компонентные классы** — `.card-aubade`, `.btn-aubade`, `.btn-aubade-accent`, `.tag-aubade`, `.admin-sidebar*`.
|
||||
|
||||
## Письма Press
|
||||
|
||||
Рассылка Second Brain Press — поверхность Email той же ДС-2. Шаблон — Listmonk template id=1 (табличная вёрстка, Arial, карта 620px с рамкой 2px `#AAAAAA`). Подробности — секция 5 канонического `DESIGN.md`.
|
||||
|
||||
## История
|
||||
|
||||
Предыдущая версия этого файла ссылалась на легаси-дизайн-систему v1 (кремовая палитра + лаванда, `~/Documents/Claude/design-system/`). Она заменена: v1 — легаси, актуальна ДС-2.
|
||||
@@ -9,6 +9,11 @@ FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
# NEXT_PUBLIC_* vars are inlined at build time
|
||||
ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY
|
||||
ENV NEXT_PUBLIC_TURNSTILE_SITE_KEY=$NEXT_PUBLIC_TURNSTILE_SITE_KEY
|
||||
ARG NEXT_PUBLIC_METRIKA_COUNTER_ID
|
||||
ENV NEXT_PUBLIC_METRIKA_COUNTER_ID=$NEXT_PUBLIC_METRIKA_COUNTER_ID
|
||||
# Generate Prisma client before build
|
||||
RUN npx prisma generate
|
||||
RUN npm run build
|
||||
|
||||
+27
@@ -275,6 +275,30 @@
|
||||
|
||||
## Бэклог (после MVP)
|
||||
|
||||
- **Миграция email-шаблонов на React Email 6 + Resend CLI 2.0** (Resend Launch Week 6, 24.04.2026):
|
||||
- React Email 6: новые шаблоны для auth и ecommerce flows (welcome, password reset, purchase confirmation, course progress) — можно взять за основу вместо своих
|
||||
- Resend CLI 2.0: локальный preview и тестирование шаблонов (`resend send --local ...`), 50+ команд
|
||||
- Embeddable open-source editor (в одну строку) — отложить, пока не требуется
|
||||
- Сейчас Этап 7 (Email-уведомления) завершён на базовой связке, задача — рефакторинг на React Email
|
||||
|
||||
- **Самостоятельная регистрация + автоматический онбординг** — два сценария входа и воронка после регистрации:
|
||||
|
||||
**Сценарии регистрации:**
|
||||
- С лендинга через покупку — пользователь оплачивает курс, аккаунт создаётся автоматически, письмо с доступом приходит сразу
|
||||
- Прямой вход на платформу — пользователь приходит по реферальной ссылке, из соцсетей, от партнёров — регистрируется сам без покупки
|
||||
|
||||
**Автоматический онбординг после регистрации:**
|
||||
- Автоназначение вводных / вотер-модулей курсов (бесплатные превью, чтобы зацепить)
|
||||
- Доступ к базовой библиотеке материалов по умолчанию (статьи, шаблоны, гайды — определяется в настройках)
|
||||
- Приветственная воронка: серия писем / уведомлений, которая ведёт к первой покупке
|
||||
- Уведомление администратора о новой регистрации (email + Telegram)
|
||||
|
||||
**Что нужно проработать:**
|
||||
- Публичная страница регистрации (+ CAPTCHA, опционально)
|
||||
- Настройка в Этапе 9: «Регистрация открыта: да/нет» + выбор вводных курсов/модулей, которые назначаются автоматически
|
||||
- Интеграция с платёжной системой: оплата на лендинге → автосоздание аккаунта → автовыдача доступа к купленному курсу
|
||||
- Разграничение: что видит гость / зарегистрированный без покупки / купивший курс
|
||||
|
||||
- Резервное копирование PostgreSQL (cron → Object Storage)
|
||||
- GitHub Actions: CI/CD pipeline (lint → build → push Docker image → deploy)
|
||||
- Сертификаты по окончании курса
|
||||
@@ -316,3 +340,6 @@
|
||||
- Записи открытых встреч и вебинаров
|
||||
- Календарь: предстоящие открытые уроки, запуски курсов, события
|
||||
- Возможно: публичный Obsidian-like граф знаний
|
||||
|
||||
- **Markdown-рендер описаний ДЗ** — сейчас `Homework.description` выводится как plain text (`whitespace-pre-wrap` в `src/components/student/homework-section.tsx`), поэтому markdown-разметка (списки, `**жирный**`, ссылки) показывается сырой. Перевести на markdown-рендер (react-markdown или общий рендерер контента урока), чтобы задания оформлялись аккуратно.
|
||||
- Контекст: при миграции ДЗ из Emdesell были артефакты форматирования (оторванные маркеры списков `• \n`, пачки пустых строк). 17.06.2026 13 ДЗ (очаг — obsidian-ai) причёсаны regex'ом под текущий plain-text рендер (бэкап `Homework_bak_20260617`). Полноценное решение — markdown-рендер.
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# Tech Debt Audit — lms-sb
|
||||
Generated: 2026-05-09
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
- **1 Critical**: middleware не работает — файл называется `proxy.ts` вместо `middleware.ts`, защита маршрутов на уровне Next.js отсутствует
|
||||
- **3 High**: двойная загрузка полной структуры курса на каждый урок; `getSettings()` вызывается дважды в root layout; нет ограничения размера загружаемых файлов
|
||||
- **0 тестов** — ни одного test-файла во всём проекте
|
||||
- **4 отладочных `console.log`** в production-коде Server Action
|
||||
- Zod установлен как зависимость, но нигде не используется — Server Actions принимают `FormData` без валидации схемы
|
||||
- 3 уязвимости npm **high** severity (next, fast-uri, fast-xml-builder)
|
||||
- `settings-form.tsx` — 506 строк, единственный god-файл, но внутренняя структура оправданна
|
||||
- Самые горячие файлы совпадают с самыми крупными: student lesson page (12 правок, 270 строк) и lesson-editor (8 правок, 408 строк) — концентрация долга
|
||||
|
||||
---
|
||||
|
||||
## Architectural Mental Model
|
||||
|
||||
LMS построена на Next.js 16 App Router с тремя зонами доступа: `(auth)`, `(student)`, `admin`/`curator`. Мутации идут через Server Actions, данные читаются в RSC. Better Auth отвечает за сессии и роли. Prisma 7 с PostgreSQL через собственный PrismaPg адаптер (обходит ограничения Turbopack).
|
||||
|
||||
Главная аномалия: middleware объявлен в `src/proxy.ts` с функцией `proxy()`, а не в `src/middleware.ts` с функцией `middleware()`. Next.js его не подхватывает. Защита работает только за счёт явных проверок сессии в каждой странице и action — что само по себе достаточно, но заявленный в CLAUDE.md "Auth middleware (защита маршрутов)" фактически не существует.
|
||||
|
||||
Второй структурный факт: при открытии страницы урока студентом происходит двойная загрузка полной структуры курса — один раз в `layout.tsx` (для sidebar), второй в `page.tsx` (для prev/next навигации). Это N+1 на уровне layout/page.
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Category | File:Line | Severity | Effort | Description | Recommendation |
|
||||
|----|----------|-----------|----------|--------|-------------|----------------|
|
||||
| F001 | Security | src/proxy.ts:1 | Critical | S | Файл `proxy.ts` не является Next.js middleware. Next.js ищет `src/middleware.ts` с экспортом `middleware`. Маршруты не защищены на уровне edge. | Переименовать файл в `src/middleware.ts`, переименовать экспорт `proxy` → `middleware` |
|
||||
| F002 | Performance | src/app/(student)/courses/[slug]/lessons/[lessonId]/page.tsx:37 | High | M | `page.tsx` загружает `lesson.module.course.modules` с вложенными уроками для prev/next nav — та же структура уже загружена в `layout.tsx:20`. Двойной DB-запрос на каждый pageview урока. | Вынести prev/next навигацию в layout или передавать через `searchParams`/context вместо повторной загрузки |
|
||||
| F003 | Performance | src/app/layout.tsx:14,27 | High | S | `getSettings()` вызывается дважды в одном компоненте — в `generateMetadata()` и в `RootLayout()`. Два одинаковых DB-запроса на каждый запрос. | Объединить в один вызов или обернуть `getSettings` в `React.cache()` |
|
||||
| F004 | Security | src/app/api/admin/upload/route.ts:1, src/app/api/student/homework-upload/route.ts:1, src/app/api/curator/audio-upload/route.ts:1 | High | S | Ни один upload endpoint не проверяет размер файла перед `file.arrayBuffer()`. Загрузка 1 ГБ файла ляжет в память Node.js. | Добавить проверку `file.size` до 50 МБ (или другого лимита) сразу после `form.get("file")` |
|
||||
| F005 | Observability | src/lib/actions/lesson-actions.ts:24,27,42,48 | Medium | S | 4 `console.log` в production Server Action. Логируют `lessonId` и статус каждого сохранения в prod-консоль. | Убрать все 4. Оставить только `console.error` в catch-блоках. |
|
||||
| F006 | Type & Contract | src/app/admin/courses/actions.ts:28-47 | Medium | M | Server Actions принимают `FormData` и читают поля через `as string` без валидации. Zod установлен, но не используется нигде в проекте. | Добавить Zod-схему на входе `createCourse` и `updateCourse`; повторить паттерн в остальных actions |
|
||||
| F007 | Dependencies | package.json | Medium | S | `npm audit` показывает 3 high severity уязвимости: `next` (сам фреймворк), `fast-uri`, `fast-xml-builder`. | `npm update next` до последнего патча; проверить влияние на остальные зависимости |
|
||||
| F008 | Dependencies | package.json | Low | S | `depcheck` находит 4 неиспользуемые зависимости: `@tailwindcss/typography`, `shadcn`, `tw-animate-css`, `zod`. Если Zod будет использован (F006), убрать оставшиеся три. | `npm remove @tailwindcss/typography shadcn tw-animate-css` |
|
||||
| F009 | Architecture | src/app/(student)/courses/[slug]/lessons/[lessonId]/page.tsx:102-106 | Low | S | Функция `formatSize` определена внутри Server Component — при каждом рендере пересоздаётся. Не ошибка, но засоряет файл. | Вынести в `src/lib/utils.ts` |
|
||||
| F010 | Consistency | src/app/admin/courses/[courseId]/actions.ts:12, src/app/admin/categories/actions.ts:11, src/app/admin/settings/actions.ts:11 | Low | S | Разные строки ошибки авторизации: `"Forbidden"` (EN), `"Нет доступа"` (RU), `"Unauthorized"` (EN). Нет единого паттерна. | Выбрать один формат и применить везде; ошибки авторизации не должны уходить клиенту как читаемый текст |
|
||||
| F011 | Security | src/app/layout.tsx:32,37 | Low | — | `dangerouslySetInnerHTML` с `headCode`/`bodyCode` из БД — admin может вставить произвольный JS. | Намеренная функция (code injection для аналитики). Задокументировать явно, что это admin-only привилегия. Добавить проверку роли на странице настроек — уже есть. |
|
||||
| F012 | Testing | — | High | L | Ни одного теста во всём проекте. Нет `*.test.*`, `*.spec.*`, нет `__tests__/`. Горячие файлы (lesson page, lesson-editor) не прикрыты ничем. | Начать с unit-тестов `src/lib/md-to-tiptap.ts` (чистая функция, высокий риск регрессии) и `src/lib/settings.ts`. Для UI — Playwright E2E на login + lesson complete flow. |
|
||||
| F013 | Consistency | src/app/(student)/courses/[slug]/layout.tsx:54, src/app/(student)/courses/[slug]/lessons/[lessonId]/page.tsx:53 | Low | S | Оба файла самостоятельно проверяют `isAdmin` для conditional DB queries с одинаковой логикой. Паттерн не вынесен. | Не критично при текущем размере, но при росте числа маршрутов станет проблемой |
|
||||
| F014 | Documentation | src/proxy.ts:1, CLAUDE.md | Medium | S | CLAUDE.md: `src/middleware.ts — Auth middleware (защита маршрутов)`. Файла не существует, существует `src/proxy.ts`. Документация врёт. | После исправления F001 — обновить CLAUDE.md |
|
||||
|
||||
---
|
||||
|
||||
## Top 5 "fix these first"
|
||||
|
||||
### 1. F001 — Переименовать proxy.ts в middleware.ts
|
||||
|
||||
```bash
|
||||
git mv src/proxy.ts src/middleware.ts
|
||||
```
|
||||
|
||||
В `src/middleware.ts`:
|
||||
```ts
|
||||
// было:
|
||||
export function proxy(request: NextRequest) { ... }
|
||||
// стало:
|
||||
export function middleware(request: NextRequest) { ... }
|
||||
```
|
||||
|
||||
`config` экспорт уже правильный — оставить как есть. Это однострочный фикс с нулевым риском регрессии.
|
||||
|
||||
---
|
||||
|
||||
### 2. F003 — Двойной `getSettings()` в root layout
|
||||
|
||||
```ts
|
||||
// src/app/layout.tsx — было: два вызова
|
||||
const settings = await getSettings(); // в generateMetadata
|
||||
const settings = await getSettings(); // в RootLayout
|
||||
|
||||
// стало: обернуть в React.cache
|
||||
// src/lib/settings.ts
|
||||
import { cache } from "react";
|
||||
export const getSettings = cache(async (): Promise<Settings> => { ... });
|
||||
```
|
||||
|
||||
Один `cache()` снимает оба дублированных запроса в рамках одного render pass.
|
||||
|
||||
---
|
||||
|
||||
### 3. F004 — Лимит размера файлов в upload endpoints
|
||||
|
||||
В каждом из 5 upload routes добавить сразу после получения файла:
|
||||
|
||||
```ts
|
||||
const MAX_BYTES = 50 * 1024 * 1024; // 50 МБ
|
||||
if (file.size > MAX_BYTES) {
|
||||
return NextResponse.json({ error: "Файл слишком большой" }, { status: 413 });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. F005 — Убрать console.log из lesson-actions.ts
|
||||
|
||||
```ts
|
||||
// src/lib/actions/lesson-actions.ts — удалить строки 24, 27, 42, 48
|
||||
console.log("[saveLesson] start", lessonId); // удалить
|
||||
console.log("[saveLesson] auth ok"); // удалить
|
||||
console.log("[saveLesson] db update ok"); // удалить
|
||||
console.log("[saveLesson] done"); // удалить
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. F002 — Устранить двойную загрузку структуры курса
|
||||
|
||||
`layout.tsx` уже загружает все модули/уроки курса для sidebar. `page.tsx` загружает ту же структуру ещё раз для prev/next навигации. Самое чистое решение — передавать `allLessons` через `searchParams` или вычислять в layout и передавать через `slot`:
|
||||
|
||||
Альтернатива проще: убрать из `page.tsx` `include: { modules: { include: { lessons } } }` и принять `prevLessonId`/`nextLessonId` как query params, которые layout прописывает в ссылки sidebar-а.
|
||||
|
||||
---
|
||||
|
||||
## Quick Wins
|
||||
|
||||
- [ ] **F001** — `git mv src/proxy.ts src/middleware.ts` + переименовать экспорт (5 минут)
|
||||
- [ ] **F003** — `import { cache } from "react"` в settings.ts, обернуть `getSettings` (10 минут)
|
||||
- [ ] **F005** — Удалить 4 строки `console.log` в lesson-actions.ts (2 минуты)
|
||||
- [ ] **F007** — `npm update next` — закрыть CVE в самом фреймворке
|
||||
- [ ] **F008** — `npm remove @tailwindcss/typography shadcn tw-animate-css` — убрать мёртвый вес
|
||||
- [ ] **F004** — Добавить `file.size` проверку в 5 upload routes (15 минут)
|
||||
|
||||
---
|
||||
|
||||
## Things that look bad but are actually fine
|
||||
|
||||
**`src/generated/prisma/`** — 20+ сгенерированных файлов в `src/`. Выглядит как мусор, но это намеренно: Prisma 7 с Turbopack требует TypeScript-клиента в src для корректной работы RSC. Объяснено в коммите `af8644e` и в memory-файле `project_lms_prisma_config.md`. Не трогать.
|
||||
|
||||
**`src/app/(student)/courses/[slug]/layout.tsx:54`** — `prisma.lessonProgress.findMany({ where: { lessonId: { in: allLessonIds } } })` загружает прогресс по всем урокам курса. Выглядит избыточно, но это единственный способ отрисовать sidebar с чекбоксами без N+1 запроса на каждый урок. Правильный паттерн.
|
||||
|
||||
**`src/lib/auth.ts:37`** — Хардкод `"https://school.second-brain.ru"` в `trustedOrigins`. Выглядит как нарушение правила "нет захардкоженных URL". На самом деле это security-критичный список и он должен быть явным, не конфигурируемым через переменные (иначе можно было бы переопределить в .env). Оставить.
|
||||
|
||||
**`catch (() => {})` в трёх местах** (email в import, S3 delete) — выглядит как проглатывание ошибок. В контексте это правильно: ошибка отправки welcome-email или удаления старого файла из S3 не должна ронять основную операцию (импорт/апгрейд файла).
|
||||
|
||||
**506 строк в `settings-form.tsx`** — формально god-файл, но это один большой конфиг-экран с однородной структурой (Section + Field + Toggle). Файл читается линейно, нет запутанной логики. Декомпозиция не добавит ясности.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **`forgot-password` и `reset-password` маршруты** не входят в `PUBLIC_ROUTES` в `proxy.ts:4`. Это намеренно — эти страницы требуют cookie для валидации токена? Или просто забыто?
|
||||
|
||||
2. **`src/app/api/admin/import-md/route.ts`** — существует, но нет UI для вызова. Мёртвый endpoint или WIP?
|
||||
|
||||
3. **QuizOption** — схема Prisma содержит `QuizOption` с `isCorrect`, но в `page.tsx` урока quiz загружается без `options` (`include: { questions: { orderBy: { order: "asc" } } }`). Тест работает только с open-ended вопросами или options загружаются где-то ещё?
|
||||
|
||||
4. **`load-test.js`** в корне репо — `k6` отмечен как missing dependency в depcheck. Это намеренно отдельный инструмент или планируется CI-интеграция?
|
||||
@@ -0,0 +1,51 @@
|
||||
# Third-Party Notices
|
||||
|
||||
This product bundles third-party software. Their licenses and copyright
|
||||
notices are reproduced below as required.
|
||||
|
||||
## force-graph
|
||||
|
||||
Used client-side in the "Obsidian Wrapped" feature to render the vault graph
|
||||
poster on an off-screen canvas. The library and its node names never leave the
|
||||
browser.
|
||||
|
||||
- Version: 1.51.4
|
||||
- License: MIT
|
||||
- Copyright (c) 2018 Vasco Asturiano
|
||||
- https://github.com/vasturiano/force-graph
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Vasco Asturiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
force-graph depends on the d3-force family (ISC) by Mike Bostock for its
|
||||
force simulation, bundled transitively.
|
||||
|
||||
## next/og (Satori + resvg)
|
||||
|
||||
Server-side share-card PNG rendering (`ImageResponse`) uses Vercel's `next/og`,
|
||||
which bundles `satori` and `@resvg/resvg-wasm` (both MPL-2.0). These are part of
|
||||
the `next` dependency and distributed under Next.js's own license terms.
|
||||
|
||||
- satori — MPL-2.0 — https://github.com/vercel/satori
|
||||
- resvg-wasm — MPL-2.0 — https://github.com/yisibl/resvg-js
|
||||
@@ -0,0 +1,159 @@
|
||||
# Student Questions — Design Spec
|
||||
_Created: 20260519_
|
||||
|
||||
## Overview
|
||||
|
||||
A support-chat feature inside the LMS. Students ask questions, school staff (admin/curator) answers. Each question is a threaded conversation with open/closed status. Includes file attachments and email notifications for all parties.
|
||||
|
||||
Also in scope: email notifications for homework submissions (new + student updates).
|
||||
|
||||
---
|
||||
|
||||
## Data Model
|
||||
|
||||
### StudentQuestion
|
||||
```
|
||||
id String @id @default(cuid())
|
||||
userId String -- student who created it
|
||||
courseId String? -- optional course context
|
||||
title String
|
||||
status QuestionStatus @default(OPEN) -- OPEN | CLOSED
|
||||
closedAt DateTime?
|
||||
closedById String? -- admin/curator who closed
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
user User
|
||||
course Course?
|
||||
messages StudentQuestionMessage[]
|
||||
```
|
||||
|
||||
### StudentQuestionMessage
|
||||
```
|
||||
id String @id @default(cuid())
|
||||
questionId String
|
||||
authorId String
|
||||
text String
|
||||
files String[] -- S3 paths: questions/{questionId}/{messageId}/{filename}
|
||||
isRead Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
question StudentQuestion
|
||||
author User
|
||||
```
|
||||
|
||||
### QuestionStatus (enum)
|
||||
```
|
||||
OPEN
|
||||
CLOSED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Routes
|
||||
|
||||
### Student
|
||||
| Route | Description |
|
||||
|---|---|
|
||||
| `(student)/questions` | List of own questions with unread indicators |
|
||||
| `(student)/questions/new` | Form to create a new question |
|
||||
| `(student)/questions/[id]` | Thread view — read messages + reply |
|
||||
|
||||
### Admin / Curator
|
||||
| Route | Description |
|
||||
|---|---|
|
||||
| `admin/questions` | Split-view: question list left, thread right |
|
||||
| `curator/questions` | Same split-view (curators have same access as admins here) |
|
||||
|
||||
---
|
||||
|
||||
## UI Behaviour
|
||||
|
||||
### Student — Questions List (`/questions`)
|
||||
- Header: "Мои вопросы" + "+ Задать вопрос" button (→ `/questions/new`)
|
||||
- Each row: title, message count, last activity, status badge (ОТКРЫТ / ЗАКРЫТ)
|
||||
- Unread indicator: black dot + "Новый ответ от школы" when school replied since last student visit
|
||||
- Closed questions: dimmed (opacity 0.7), grey badge
|
||||
- Active (unread) questions: bold border, bold title
|
||||
|
||||
### Student — Thread (`/questions/[id]`)
|
||||
- Header: question title, created date, status, "← Все вопросы" link
|
||||
- Message bubbles: student messages left (#E8E8E0), school messages right (#F5F5F0 + green border #E8F0D8)
|
||||
- New school message: bold label "🔵 новое" in timestamp
|
||||
- File attachments shown inline under message text (📎 filename · size)
|
||||
- Reply form at bottom: textarea + attach button + send button
|
||||
- Attachment types: jpg, png, pdf, md · max 10 MB
|
||||
- Student CAN reply to closed questions (creates new message, does NOT reopen question)
|
||||
|
||||
### Admin/Curator — Split View (`/admin/questions`, `/curator/questions`)
|
||||
- Left panel (45%): tab filter "Открытые / Закрытые", question list sorted by last activity
|
||||
- Unread: red dot, green-tinted background, bold student name
|
||||
- Right panel (55%): selected thread with full message history + reply form + "✓ Закрыть вопрос" button
|
||||
- Only admin/curator can close a question
|
||||
- Closing a question does NOT prevent further messages
|
||||
|
||||
---
|
||||
|
||||
## Notifications
|
||||
|
||||
### → School (admin + all curators)
|
||||
| Trigger | Channel |
|
||||
|---|---|
|
||||
| New question created | Email + admin sidebar badge (count of unread questions) |
|
||||
| Student adds message to existing question | Email |
|
||||
|
||||
### → Student
|
||||
| Trigger | Channel |
|
||||
|---|---|
|
||||
| Admin/curator replies to question | Email |
|
||||
|
||||
### Admin Badge
|
||||
- Sidebar badge shows count of questions with unread messages (school hasn't seen yet)
|
||||
- Separate from homework badge
|
||||
|
||||
### Email for Homework (added to scope)
|
||||
| Trigger | Recipient |
|
||||
|---|---|
|
||||
| New HomeworkSubmission created | Admin + all curators |
|
||||
| Student updates existing submission (adds text/file) | Admin + all curators |
|
||||
|
||||
---
|
||||
|
||||
## File Storage
|
||||
|
||||
- Path pattern: `questions/{questionId}/{messageId}/{filename}`
|
||||
- Reuse existing S3 upload infrastructure (`src/lib/s3.ts`)
|
||||
- Allowed: jpg, png, pdf, md
|
||||
- Max size: 10 MB per file
|
||||
- No limit on number of files per message (reasonable: 5)
|
||||
|
||||
---
|
||||
|
||||
## Read Tracking
|
||||
|
||||
- `isRead` flag per message, set to `true` when the OTHER party opens the thread
|
||||
- Student opens `/questions/[id]` → all school messages in that thread marked `isRead = true`
|
||||
- Admin/curator opens a question in split-view → all student messages marked `isRead = true`
|
||||
- Admin badge recalculates on each page load (count questions where latest student message is unread)
|
||||
|
||||
---
|
||||
|
||||
## API Routes
|
||||
|
||||
```
|
||||
POST /api/questions -- create question
|
||||
GET /api/questions -- list own questions (student) or all (admin/curator)
|
||||
GET /api/questions/[id] -- get question + messages
|
||||
POST /api/questions/[id]/messages -- add message + files
|
||||
PATCH /api/questions/[id]/close -- close question (admin/curator only)
|
||||
POST /api/upload/question-file -- upload attachment, returns S3 path
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope (this iteration)
|
||||
- Question categories / tags
|
||||
- Assigning question to a specific curator
|
||||
- Email threading (reply-to email to answer)
|
||||
- Push/browser notifications
|
||||
- Question templates
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,451 @@
|
||||
# LMS Staging Environment Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Развернуть полноценный staging-стенд LMS на `staging.school.second-brain.ru` — изолированный от prod, с автоматическим применением миграций и отключёнными email-уведомлениями.
|
||||
|
||||
**Architecture:** Hetzner VPS (178.104.27.196, 16 GB RAM), отдельный Docker Compose стек в `/root/digital-household/lms-staging/` с собственным postgres-контейнером. Caddy на том же сервере роутит `staging.school.second-brain.ru → localhost:3011`. Сборочный репо — `/root/lms-staging-build/`, тег образа — `lms-staging-app:latest`. Деплой в staging производится отдельным скриптом `deploy-staging.sh` независимо от prod.
|
||||
|
||||
**Tech Stack:** Docker Compose, Next.js 16 (App Router), PostgreSQL 16, Caddy, Gitea (git.second-brain.ru/admins/lms-sb)
|
||||
|
||||
---
|
||||
|
||||
## Файловая структура
|
||||
|
||||
```
|
||||
Hetzner VPS (root@178.104.27.196):
|
||||
├── /root/lms-staging-build/ ← git clone (сборочная директория)
|
||||
│ └── [полный репо lms-sb]
|
||||
├── /root/digital-household/lms-staging/
|
||||
│ ├── docker-compose.yml ← создаём (app + db)
|
||||
│ └── .env ← создаём (staging-специфичный)
|
||||
└── /root/digital-household/caddy-proxy/
|
||||
└── Caddyfile ← добавляем блок staging
|
||||
|
||||
Mac:
|
||||
└── ~/Documents/Claude/scripts/deploy-staging.sh ← создаём
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Клонировать репо на Hetzner
|
||||
|
||||
**Files:**
|
||||
- Создаём: `/root/lms-staging-build/` на сервере
|
||||
|
||||
- [ ] **Шаг 1.1: SSH на Hetzner и клонировать репо**
|
||||
|
||||
Нужен токен Gitea. Он хранится в уже существующем git remote на сервере. Достанем его:
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "git -C /root/digital-household/lms-sb remote get-url origin 2>/dev/null || echo 'no-remote'"
|
||||
```
|
||||
|
||||
Если remote есть — берём URL из него. Если нет — токен в `~/.config/secrets/` на Mac (найти файл с `GITEA_TOKEN` или `git.second-brain.ru`).
|
||||
|
||||
- [ ] **Шаг 1.2: Клонировать**
|
||||
|
||||
```bash
|
||||
GITEA_URL=$(ssh root@178.104.27.196 "git -C /root/digital-household/lms-sb remote get-url origin 2>/dev/null")
|
||||
# Пример: https://admins:TOKEN@git.second-brain.ru/admins/lms-sb.git
|
||||
|
||||
ssh root@178.104.27.196 "git clone ${GITEA_URL} /root/lms-staging-build"
|
||||
```
|
||||
|
||||
- [ ] **Шаг 1.3: Проверить**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "ls /root/lms-staging-build/src && git -C /root/lms-staging-build log --oneline -3"
|
||||
```
|
||||
|
||||
Ожидаем: список файлов и последние 3 коммита.
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Создать staging docker-compose.yml
|
||||
|
||||
**Files:**
|
||||
- Создаём: `/root/digital-household/lms-staging/docker-compose.yml`
|
||||
|
||||
- [ ] **Шаг 2.1: Создать директорию**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "mkdir -p /root/digital-household/lms-staging"
|
||||
```
|
||||
|
||||
- [ ] **Шаг 2.2: Записать docker-compose.yml**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cat > /root/digital-household/lms-staging/docker-compose.yml" << 'COMPOSE'
|
||||
services:
|
||||
app:
|
||||
image: lms-staging-app:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3011:3000"
|
||||
env_file: .env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: lms_staging_user
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||
POSTGRES_DB: lms_staging_db
|
||||
volumes:
|
||||
- staging_postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U lms_staging_user -d lms_staging_db"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
volumes:
|
||||
staging_postgres_data:
|
||||
COMPOSE
|
||||
```
|
||||
|
||||
- [ ] **Шаг 2.3: Проверить**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cat /root/digital-household/lms-staging/docker-compose.yml"
|
||||
```
|
||||
|
||||
Ожидаем: полный YAML без артефактов heredoc.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Залить staging .env на сервер
|
||||
|
||||
**Files:**
|
||||
- Создаём: `/root/digital-household/lms-staging/.env`
|
||||
|
||||
Все креды уже сгенерированы и хранятся в `~/.config/secrets/lms-staging.env` на Mac.
|
||||
|
||||
- [ ] **Шаг 3.1: Скопировать .env на сервер**
|
||||
|
||||
```bash
|
||||
scp ~/.config/secrets/lms-staging.env root@178.104.27.196:/root/digital-household/lms-staging/.env
|
||||
```
|
||||
|
||||
- [ ] **Шаг 3.2: Установить права**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "chmod 600 /root/digital-household/lms-staging/.env"
|
||||
```
|
||||
|
||||
- [ ] **Шаг 3.3: Проверить**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "grep -E '^BETTER_AUTH_URL|^RESEND_API_KEY|^DB_PASSWORD' /root/digital-household/lms-staging/.env"
|
||||
```
|
||||
|
||||
Ожидаем:
|
||||
```
|
||||
BETTER_AUTH_URL=https://staging.school.second-brain.ru
|
||||
RESEND_API_KEY=
|
||||
DB_PASSWORD=38002343e67efd057559d8f073d45892
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Собрать Docker-образ и запустить staging
|
||||
|
||||
**Files:**
|
||||
- Используем `/root/lms-staging-build/` для сборки
|
||||
|
||||
- [ ] **Шаг 4.1: Собрать образ**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cd /root/lms-staging-build && docker build -t lms-staging-app:latest . 2>&1 | tail -5"
|
||||
```
|
||||
|
||||
Ожидаем: `Successfully built ...` или `naming to docker.io/library/lms-staging-app:latest done`.
|
||||
|
||||
Время: ~2–4 минуты.
|
||||
|
||||
- [ ] **Шаг 4.2: Запустить контейнеры**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cd /root/digital-household/lms-staging && docker compose up -d"
|
||||
```
|
||||
|
||||
Ожидаем: `Container lms-staging-db-1 Started`, `Container lms-staging-app-1 Started`.
|
||||
|
||||
- [ ] **Шаг 4.3: Проверить логи (через 10 сек)**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "sleep 10 && docker logs lms-staging-app-1 --tail 20"
|
||||
```
|
||||
|
||||
Ожидаем в логах:
|
||||
- `Running database migrations...`
|
||||
- `No pending migrations to apply.` (или список применённых)
|
||||
- `Starting Next.js...`
|
||||
- `✓ Ready in 0ms`
|
||||
|
||||
Если вместо этого ошибка `DATABASE_URL` или `connection refused` — проверить `.env`: `docker exec lms-staging-app-1 env | grep DATABASE`.
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Настроить Caddy
|
||||
|
||||
**Files:**
|
||||
- Изменяем: `/root/digital-household/caddy-proxy/Caddyfile`
|
||||
|
||||
- [ ] **Шаг 5.1: Посмотреть текущий конец Caddyfile**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "tail -10 /root/digital-household/caddy-proxy/Caddyfile"
|
||||
```
|
||||
|
||||
- [ ] **Шаг 5.2: Добавить блок для staging**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cat >> /root/digital-household/caddy-proxy/Caddyfile" << 'CADDY'
|
||||
|
||||
staging.school.second-brain.ru {
|
||||
reverse_proxy http://localhost:3011
|
||||
}
|
||||
CADDY
|
||||
```
|
||||
|
||||
- [ ] **Шаг 5.3: Перезагрузить Caddy**
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "docker exec caddy-proxy caddy reload --config /etc/caddy/Caddyfile 2>&1"
|
||||
```
|
||||
|
||||
Ожидаем: пустой вывод (успех) или `Successfully loaded new configuration`.
|
||||
|
||||
Если ошибка синтаксиса — проверить: `docker exec caddy-proxy caddy validate --config /etc/caddy/Caddyfile`.
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Добавить DNS-запись
|
||||
|
||||
DNS для `*.school.second-brain.ru` и `school.second-brain.ru` нужно проверить — где управляется зона.
|
||||
|
||||
- [ ] **Шаг 6.1: Проверить текущий DNS**
|
||||
|
||||
```bash
|
||||
dig +short staging.school.second-brain.ru
|
||||
dig +short school.second-brain.ru
|
||||
```
|
||||
|
||||
Если `staging.school.second-brain.ru` не резолвится — нужно добавить A-запись.
|
||||
|
||||
- [ ] **Шаг 6.2: Добавить A-запись**
|
||||
|
||||
В панели DNS (Hetzner DNS Console или CloudFlare — где управляется зона `second-brain.ru`):
|
||||
|
||||
```
|
||||
Тип: A
|
||||
Имя: staging.school
|
||||
Значение: 178.104.27.196
|
||||
TTL: 300
|
||||
```
|
||||
|
||||
- [ ] **Шаг 6.3: Дождаться распространения**
|
||||
|
||||
```bash
|
||||
# Через 1-2 минуты:
|
||||
dig +short staging.school.second-brain.ru
|
||||
```
|
||||
|
||||
Ожидаем: `178.104.27.196`
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Проверить работу staging end-to-end
|
||||
|
||||
- [ ] **Шаг 7.1: HTTP проверка**
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w "%{http_code}" https://staging.school.second-brain.ru/login
|
||||
```
|
||||
|
||||
Ожидаем: `200`
|
||||
|
||||
- [ ] **Шаг 7.2: Войти под тестовым аккаунтом**
|
||||
|
||||
Открыть `https://staging.school.second-brain.ru/login` в браузере.
|
||||
|
||||
БД пустая, но seed создаёт admin-аккаунт. Запустить seed:
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cd /root/lms-staging-build && docker compose -f /root/digital-household/lms-staging/docker-compose.yml exec app npx prisma db seed 2>&1 | tail -5"
|
||||
```
|
||||
|
||||
После seed входить: `admin@second-brain.ru` / `Password123!`
|
||||
|
||||
- [ ] **Шаг 7.3: Проверить что email не уходит**
|
||||
|
||||
В staging при регистрации `/api/auth/sign-up` не должно быть ошибок Resend (только `RESEND_API_KEY` пустой — письмо тихо не отправляется, регистрация всё равно работает).
|
||||
|
||||
Убедиться в логах:
|
||||
```bash
|
||||
ssh root@178.104.27.196 "docker logs lms-staging-app-1 --since 5m 2>&1 | grep -i 'resend\|email\|error' | head -10"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: Скрипт deploy-to-staging и документация
|
||||
|
||||
**Files:**
|
||||
- Создаём: `~/Documents/Claude/scripts/deploy-staging.sh`
|
||||
- Создаём: `SBT/02-Стандарты/Процессы/lms-staging-deploy.md`
|
||||
|
||||
- [ ] **Шаг 8.1: Написать скрипт deploy-staging.sh**
|
||||
|
||||
```bash
|
||||
cat > ~/Documents/Claude/scripts/deploy-staging.sh << 'SCRIPT'
|
||||
#!/usr/bin/env bash
|
||||
# Deploy LMS to staging (staging.school.second-brain.ru)
|
||||
# Usage: bash deploy-staging.sh
|
||||
set -euo pipefail
|
||||
|
||||
STAGING_SERVER="root@178.104.27.196"
|
||||
BUILD_DIR="/root/lms-staging-build"
|
||||
COMPOSE_DIR="/root/digital-household/lms-staging"
|
||||
LOG_LINES=15
|
||||
|
||||
echo "=== LMS Staging Deploy ==="
|
||||
|
||||
echo "→ Pulling latest code..."
|
||||
ssh "$STAGING_SERVER" "cd ${BUILD_DIR} && git pull 2>&1 | tail -5"
|
||||
|
||||
echo "→ Building image..."
|
||||
ssh "$STAGING_SERVER" "cd ${BUILD_DIR} && docker build -t lms-staging-app:latest . 2>&1 | tail -5"
|
||||
|
||||
echo "→ Restarting app..."
|
||||
ssh "$STAGING_SERVER" "cd ${COMPOSE_DIR} && docker compose up -d app"
|
||||
|
||||
echo "→ Waiting 10s for startup..."
|
||||
sleep 10
|
||||
|
||||
echo "→ Logs:"
|
||||
ssh "$STAGING_SERVER" "docker logs lms-staging-app-1 --tail ${LOG_LINES} 2>&1"
|
||||
|
||||
echo "→ Health check..."
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" https://staging.school.second-brain.ru/login)
|
||||
if [ "$CODE" = "200" ]; then
|
||||
echo "✅ Staging OK — https://staging.school.second-brain.ru (HTTP $CODE)"
|
||||
else
|
||||
echo "❌ Staging returned HTTP $CODE"
|
||||
exit 1
|
||||
fi
|
||||
SCRIPT
|
||||
chmod +x ~/Documents/Claude/scripts/deploy-staging.sh
|
||||
```
|
||||
|
||||
- [ ] **Шаг 8.2: Тестовый запуск скрипта**
|
||||
|
||||
```bash
|
||||
bash ~/Documents/Claude/scripts/deploy-staging.sh
|
||||
```
|
||||
|
||||
Ожидаем в конце: `✅ Staging OK — https://staging.school.second-brain.ru (HTTP 200)`
|
||||
|
||||
- [ ] **Шаг 8.3: Создать SBT-заметку**
|
||||
|
||||
Создать файл `SBT/02-Стандарты/Процессы/lms-staging-deploy.md` со следующим содержимым:
|
||||
|
||||
```markdown
|
||||
---
|
||||
type: process
|
||||
title: "LMS — Деплой в staging"
|
||||
status: active
|
||||
created: 20260525
|
||||
updated: 20260525
|
||||
tags: [lms, staging, deploy, docker]
|
||||
related: ["[[Процессы/lms-deploy]]", "[[Сервисы/hoster-kz]]", "[[Сервисы/vps-hetzner]]"]
|
||||
---
|
||||
|
||||
# Процесс: Деплой LMS в staging
|
||||
|
||||
## Когда применяется
|
||||
|
||||
Перед выкаткой любых изменений в prod — сначала деплоим в staging и проверяем.
|
||||
Также: тестирование миграций БД, проверка новых фич, отладка.
|
||||
|
||||
## Ключевые факты
|
||||
|
||||
| Что | Где |
|
||||
|---|---|
|
||||
| URL | https://staging.school.second-brain.ru |
|
||||
| Сервер | Hetzner Antigravity-1 (178.104.27.196) |
|
||||
| Compose | `/root/digital-household/lms-staging/` |
|
||||
| Build dir | `/root/lms-staging-build/` |
|
||||
| Порт | 3011 |
|
||||
| БД | `lms_staging_db` (отдельный контейнер) |
|
||||
| Email | Отключён (RESEND_API_KEY пустой) |
|
||||
| S3 | Тот же бакет что и prod |
|
||||
|
||||
## Деплой одной командой (с Mac)
|
||||
|
||||
```bash
|
||||
bash ~/Documents/Claude/scripts/deploy-staging.sh
|
||||
```
|
||||
|
||||
## Ручные шаги (если нужно)
|
||||
|
||||
```bash
|
||||
# На сервере
|
||||
cd /root/lms-staging-build && git pull
|
||||
docker build -t lms-staging-app:latest .
|
||||
cd /root/digital-household/lms-staging && docker compose up -d app
|
||||
docker logs lms-staging-app-1 --tail 20
|
||||
```
|
||||
|
||||
## Откат
|
||||
|
||||
```bash
|
||||
ssh root@178.104.27.196 "cd /root/digital-household/lms-staging && docker compose down"
|
||||
```
|
||||
|
||||
## Восстановить prod-данные в staging (для отладки)
|
||||
|
||||
```bash
|
||||
# 1. Взять свежий дамп с 3TB или Storage Box
|
||||
# 2. Залить в staging-базу
|
||||
DUMP="/Volumes/3TB/Second Brain Production/LMS Backups/lms-db-YYYYMMDD-HHMM.sql.gz"
|
||||
scp "$DUMP" root@178.104.27.196:/tmp/staging-restore.sql.gz
|
||||
ssh root@178.104.27.196 \
|
||||
"zcat /tmp/staging-restore.sql.gz | docker exec -i lms-staging-db-1 psql -U lms_staging_user lms_staging_db"
|
||||
```
|
||||
|
||||
## Связанные процессы
|
||||
|
||||
- [[Процессы/lms-deploy]] — деплой в prod (после проверки на staging)
|
||||
- [[Процессы/lms-disaster-recovery]]
|
||||
```
|
||||
|
||||
- [ ] **Шаг 8.4: Коммит скрипта**
|
||||
|
||||
```bash
|
||||
cd ~/Documents/Claude/lms-system
|
||||
git add docs/
|
||||
git commit -m "Add LMS staging environment plan and deploy script"
|
||||
git push origin HEAD
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Чек-лист финальной проверки
|
||||
|
||||
- [ ] `https://staging.school.second-brain.ru/login` → 200
|
||||
- [ ] Войти под тестовым аккаунтом
|
||||
- [ ] Курс открывается
|
||||
- [ ] Email НЕ уходит при регистрации (проверить логи)
|
||||
- [ ] `bash deploy-staging.sh` завершается с `✅ Staging OK`
|
||||
- [ ] SBT-заметка создана
|
||||
|
||||
---
|
||||
|
||||
## Ожидаемое время
|
||||
|
||||
~30–40 минут (основное время — сборка Docker-образа ~3–5 мин).
|
||||
@@ -0,0 +1 @@
|
||||
{"0": "Admin Categories & UI Library", "1": "Auth Flow & App Settings", "2": "Markdown→TipTap Import", "3": "Email Notifications & Q&A API", "4": "Student Lesson Player", "5": "Auth/Session & Student Profile", "6": "Lesson Editor & Quiz Tools", "7": "Admin User Detail (Balance/Contact)", "8": "Next.js Config", "9": "Course Tree & Module Sorting", "10": "CSV Import/Export", "11": "Curator Homework Review", "12": "Components Manifest", "13": "Prisma Client & Dashboards", "14": "Admin Users List & Enroll", "15": "TSConfig", "16": "S3 Upload Routes", "17": "Module Page & Lesson Sorting", "18": "Cross-cutting Course Actions", "19": "Create-User Form", "20": "Load Testing", "21": "Admin Comments Moderation", "22": "Admin Questions Split View", "23": "Bridge: Shared Libs (auth/prisma/email)", "24": "Module Actions Standalone", "25": "Student Course Sidebar Layout", "26": "Question Thread UI", "27": "Admin/Curator Dashboards", "28": "Homework Filters", "29": "Student New Question Page", "30": "Prisma Seed", "31": "Admin Homework Actions", "32": "Middleware", "33": "Email→Homework→Questions Bridge", "34": "Tech Docs & Prisma Schema", "35": "Lesson Actions Lib", "36": "Student Comment Actions", "37": "Lesson Actions + Prisma Bridge", "38": "Project Docs (CLAUDE/ROADMAP)", "39": "Homework Editor↔Actions Pair", "40": "File-format Badge & Files Manager", "41": "Auth↔Middleware Pair", "42": "ESLint Config", "43": "PostCSS Config", "44": "Lesson Editor↔md-to-tiptap", "45": "Sortable Lessons↔Module Actions", "46": "Quiz Editor↔Quiz Actions", "47": "Backup Script", "48": "Verify Email Page", "49": "Maintenance Page", "50": "Sonner Toast", "51": "User Enrollment Pair", "52": "Community 52", "53": "Community 53", "54": "Community 54", "55": "Community 55", "56": "Community 56", "57": "Community 57", "58": "Community 58", "59": "Community 59", "60": "Community 60", "61": "Community 61", "62": "Community 62", "63": "Community 63", "64": "Community 64", "65": "Community 65", "66": "Community 66", "67": "Community 67", "68": "Community 68", "69": "Community 69", "70": "Community 70", "71": "Community 71", "72": "Community 72", "73": "Community 73", "74": "Community 74", "75": "Community 75", "76": "Community 76", "77": "Community 77", "78": "Community 78"}
|
||||
@@ -0,0 +1 @@
|
||||
/Users/dementiy/.local/share/uv/tools/graphifyy/bin/python3
|
||||
@@ -0,0 +1 @@
|
||||
/Users/dementiy/Documents/Claude/lms-system
|
||||
@@ -0,0 +1,253 @@
|
||||
# Graph Report - /Users/dementiy/Documents/Claude/lms-system (2026-05-20)
|
||||
|
||||
## Corpus Check
|
||||
- 189 files · ~66,515 words
|
||||
- Verdict: corpus is large enough that graph structure adds value.
|
||||
|
||||
## Summary
|
||||
- 747 nodes · 1213 edges · 79 communities (37 shown, 42 thin omitted)
|
||||
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS · INFERRED: 4 edges (avg confidence: 0.85)
|
||||
- Token cost: 193,752 input · 7,975 output
|
||||
|
||||
## Community Hubs (Navigation)
|
||||
- [[_COMMUNITY_Admin Categories & UI Library|Admin Categories & UI Library]]
|
||||
- [[_COMMUNITY_Auth Flow & App Settings|Auth Flow & App Settings]]
|
||||
- [[_COMMUNITY_Markdown→TipTap Import|Markdown→TipTap Import]]
|
||||
- [[_COMMUNITY_Email Notifications & Q&A API|Email Notifications & Q&A API]]
|
||||
- [[_COMMUNITY_Student Lesson Player|Student Lesson Player]]
|
||||
- [[_COMMUNITY_AuthSession & Student Profile|Auth/Session & Student Profile]]
|
||||
- [[_COMMUNITY_Lesson Editor & Quiz Tools|Lesson Editor & Quiz Tools]]
|
||||
- [[_COMMUNITY_Admin User Detail (BalanceContact)|Admin User Detail (Balance/Contact)]]
|
||||
- [[_COMMUNITY_Next.js Config|Next.js Config]]
|
||||
- [[_COMMUNITY_Course Tree & Module Sorting|Course Tree & Module Sorting]]
|
||||
- [[_COMMUNITY_CSV ImportExport|CSV Import/Export]]
|
||||
- [[_COMMUNITY_Curator Homework Review|Curator Homework Review]]
|
||||
- [[_COMMUNITY_Components Manifest|Components Manifest]]
|
||||
- [[_COMMUNITY_Prisma Client & Dashboards|Prisma Client & Dashboards]]
|
||||
- [[_COMMUNITY_Admin Users List & Enroll|Admin Users List & Enroll]]
|
||||
- [[_COMMUNITY_TSConfig|TSConfig]]
|
||||
- [[_COMMUNITY_S3 Upload Routes|S3 Upload Routes]]
|
||||
- [[_COMMUNITY_Module Page & Lesson Sorting|Module Page & Lesson Sorting]]
|
||||
- [[_COMMUNITY_Cross-cutting Course Actions|Cross-cutting Course Actions]]
|
||||
- [[_COMMUNITY_Create-User Form|Create-User Form]]
|
||||
- [[_COMMUNITY_Load Testing|Load Testing]]
|
||||
- [[_COMMUNITY_Admin Comments Moderation|Admin Comments Moderation]]
|
||||
- [[_COMMUNITY_Admin Questions Split View|Admin Questions Split View]]
|
||||
- [[_COMMUNITY_Bridge Shared Libs (authprismaemail)|Bridge: Shared Libs (auth/prisma/email)]]
|
||||
- [[_COMMUNITY_Module Actions Standalone|Module Actions Standalone]]
|
||||
- [[_COMMUNITY_Student Course Sidebar Layout|Student Course Sidebar Layout]]
|
||||
- [[_COMMUNITY_Question Thread UI|Question Thread UI]]
|
||||
- [[_COMMUNITY_AdminCurator Dashboards|Admin/Curator Dashboards]]
|
||||
- [[_COMMUNITY_Homework Filters|Homework Filters]]
|
||||
- [[_COMMUNITY_Student New Question Page|Student New Question Page]]
|
||||
- [[_COMMUNITY_Prisma Seed|Prisma Seed]]
|
||||
- [[_COMMUNITY_Admin Homework Actions|Admin Homework Actions]]
|
||||
- [[_COMMUNITY_Middleware|Middleware]]
|
||||
- [[_COMMUNITY_Email→Homework→Questions Bridge|Email→Homework→Questions Bridge]]
|
||||
- [[_COMMUNITY_Tech Docs & Prisma Schema|Tech Docs & Prisma Schema]]
|
||||
- [[_COMMUNITY_Lesson Actions Lib|Lesson Actions Lib]]
|
||||
- [[_COMMUNITY_Lesson Actions + Prisma Bridge|Lesson Actions + Prisma Bridge]]
|
||||
- [[_COMMUNITY_Project Docs (CLAUDEROADMAP)|Project Docs (CLAUDE/ROADMAP)]]
|
||||
- [[_COMMUNITY_Homework Editor↔Actions Pair|Homework Editor↔Actions Pair]]
|
||||
- [[_COMMUNITY_File-format Badge & Files Manager|File-format Badge & Files Manager]]
|
||||
- [[_COMMUNITY_Auth↔Middleware Pair|Auth↔Middleware Pair]]
|
||||
- [[_COMMUNITY_ESLint Config|ESLint Config]]
|
||||
- [[_COMMUNITY_PostCSS Config|PostCSS Config]]
|
||||
- [[_COMMUNITY_Lesson Editor↔md-to-tiptap|Lesson Editor↔md-to-tiptap]]
|
||||
- [[_COMMUNITY_Sortable Lessons↔Module Actions|Sortable Lessons↔Module Actions]]
|
||||
- [[_COMMUNITY_Quiz Editor↔Quiz Actions|Quiz Editor↔Quiz Actions]]
|
||||
- [[_COMMUNITY_User Enrollment Pair|User Enrollment Pair]]
|
||||
- [[_COMMUNITY_Community 54|Community 54]]
|
||||
- [[_COMMUNITY_Community 55|Community 55]]
|
||||
- [[_COMMUNITY_Community 56|Community 56]]
|
||||
- [[_COMMUNITY_Community 57|Community 57]]
|
||||
- [[_COMMUNITY_Community 58|Community 58]]
|
||||
- [[_COMMUNITY_Community 59|Community 59]]
|
||||
- [[_COMMUNITY_Community 60|Community 60]]
|
||||
- [[_COMMUNITY_Community 61|Community 61]]
|
||||
- [[_COMMUNITY_Community 62|Community 62]]
|
||||
- [[_COMMUNITY_Community 63|Community 63]]
|
||||
- [[_COMMUNITY_Community 64|Community 64]]
|
||||
- [[_COMMUNITY_Community 65|Community 65]]
|
||||
- [[_COMMUNITY_Community 66|Community 66]]
|
||||
- [[_COMMUNITY_Community 67|Community 67]]
|
||||
- [[_COMMUNITY_Community 68|Community 68]]
|
||||
- [[_COMMUNITY_Community 69|Community 69]]
|
||||
- [[_COMMUNITY_Community 70|Community 70]]
|
||||
- [[_COMMUNITY_Community 71|Community 71]]
|
||||
- [[_COMMUNITY_Community 72|Community 72]]
|
||||
- [[_COMMUNITY_Community 73|Community 73]]
|
||||
- [[_COMMUNITY_Community 74|Community 74]]
|
||||
- [[_COMMUNITY_Community 75|Community 75]]
|
||||
- [[_COMMUNITY_Community 76|Community 76]]
|
||||
- [[_COMMUNITY_Community 77|Community 77]]
|
||||
- [[_COMMUNITY_Community 78|Community 78]]
|
||||
|
||||
## God Nodes (most connected - your core abstractions)
|
||||
1. `auth` - 55 edges
|
||||
2. `cn()` - 39 edges
|
||||
3. `compilerOptions` - 16 edges
|
||||
4. `getSetting()` - 15 edges
|
||||
5. `uploadFile()` - 14 edges
|
||||
6. `getSchoolName()` - 13 edges
|
||||
7. `Prisma Client` - 13 edges
|
||||
8. `getResend()` - 12 edges
|
||||
9. `base()` - 12 edges
|
||||
10. `btn()` - 12 edges
|
||||
|
||||
## Surprising Connections (you probably didn't know these)
|
||||
- `cn()` --calls--> `clsx` [INFERRED]
|
||||
src/lib/utils.ts → package.json
|
||||
- `mdToTiptap()` --calls--> `unified` [INFERRED]
|
||||
src/lib/md-to-tiptap.ts → package.json
|
||||
- `Claude Project Rules` --references--> `Project Roadmap` [EXTRACTED]
|
||||
CLAUDE.md → ROADMAP.md
|
||||
- `Technical Documentation` --references--> `Prisma Schema` [EXTRACTED]
|
||||
TECHNICAL.md → prisma/schema.prisma
|
||||
- `generateMetadata()` --calls--> `getSettings` [EXTRACTED]
|
||||
src/app/layout.tsx → src/lib/settings.ts
|
||||
|
||||
## Communities (79 total, 42 thin omitted)
|
||||
|
||||
### Community 0 - "Admin Categories & UI Library"
|
||||
Cohesion: 0.06
|
||||
Nodes (49): CategoryRow(), Props, Category, Course, CreateCourseDialog(), createCategory(), deleteCategory(), requireAdmin() (+41 more)
|
||||
|
||||
### Community 1 - "Auth Flow & App Settings"
|
||||
Cohesion: 0.07
|
||||
Nodes (29): AdminNav(), links, AdminShell(), focusHandlers, inputStyle, SettingsForm(), StopImpersonateBanner(), firaMono (+21 more)
|
||||
|
||||
### Community 2 - "Markdown→TipTap Import"
|
||||
Cohesion: 0.05
|
||||
Nodes (41): POST(), convertBlock(), convertInline(), Mark, MdastNode, mdToTiptap(), TipTapNode, dependencies (+33 more)
|
||||
|
||||
### Community 3 - "Email Notifications & Q&A API"
|
||||
Cohesion: 0.15
|
||||
Nodes (30): createModule(), deleteModule(), grantAccess(), reorderModules(), requireAdmin(), revokeAccess(), updateModule(), HomeworkFile (+22 more)
|
||||
|
||||
### Community 4 - "Student Lesson Player"
|
||||
Cohesion: 0.08
|
||||
Nodes (24): addComment(), deleteComment(), HomeworkFile, submitHomework(), submitQuizAttempt(), toggleLessonProgress(), parseNotificationEmails(), KinescopePlayer() (+16 more)
|
||||
|
||||
### Community 5 - "Auth/Session & Student Profile"
|
||||
Cohesion: 0.09
|
||||
Nodes (6): { GET, POST }, auth, Session, User, metadata, POST()
|
||||
|
||||
### Community 6 - "Lesson Editor & Quiz Tools"
|
||||
Cohesion: 0.09
|
||||
Nodes (22): deleteHomework(), requireAdmin(), saveHomework(), deleteQuiz(), requireAdmin(), saveQuiz(), HomeworkEditor(), Props (+14 more)
|
||||
|
||||
### Community 7 - "Admin User Detail (Balance/Contact)"
|
||||
Cohesion: 0.10
|
||||
Nodes (25): bulkGrantAccess(), requireAdmin(), revokeUserAccess(), focusHandlers, formatAmount(), inputStyle, Props, Transaction (+17 more)
|
||||
|
||||
### Community 8 - "Next.js Config"
|
||||
Cohesion: 0.07
|
||||
Nodes (26): nextConfig, devDependencies, dotenv, eslint, eslint-config-next, prisma, tailwindcss, @tailwindcss/postcss (+18 more)
|
||||
|
||||
### Community 9 - "Course Tree & Module Sorting"
|
||||
Cohesion: 0.13
|
||||
Nodes (19): createModule(), deleteModule(), grantAccess(), reorderModules(), requireAdmin(), revokeAccess(), updateModule(), CourseEditForm() (+11 more)
|
||||
|
||||
### Community 10 - "CSV Import/Export"
|
||||
Cohesion: 0.12
|
||||
Nodes (18): Course, CsvExporter(), focusHandlers, inputStyle, Course, CsvImporter(), focusHandlers, inputStyle (+10 more)
|
||||
|
||||
### Community 11 - "Curator Homework Review"
|
||||
Cohesion: 0.12
|
||||
Nodes (14): AudioRecorder(), AudioRecorderProps, ContentViewer(), asBool(), deleteSubmission(), requireCurator(), setReviewing(), submitFeedback() (+6 more)
|
||||
|
||||
### Community 12 - "Components Manifest"
|
||||
Cohesion: 0.09
|
||||
Nodes (21): aliases, components, hooks, lib, ui, utils, iconLibrary, menuAccent (+13 more)
|
||||
|
||||
### Community 13 - "Prisma Client & Dashboards"
|
||||
Cohesion: 0.10
|
||||
Nodes (7): Props, requireAdmin(), saveLesson(), globalForPrisma, Props, metadata, Props
|
||||
|
||||
### Community 14 - "Admin Users List & Enroll"
|
||||
Cohesion: 0.12
|
||||
Nodes (14): Course, Props, QuickEnrollButton(), inputStyle, UsersSearch(), Enrollment, roleLabel, roleVariant (+6 more)
|
||||
|
||||
### Community 15 - "TSConfig"
|
||||
Cohesion: 0.10
|
||||
Nodes (19): compilerOptions, allowJs, esModuleInterop, incremental, isolatedModules, jsx, lib, module (+11 more)
|
||||
|
||||
### Community 16 - "S3 Upload Routes"
|
||||
Cohesion: 0.22
|
||||
Nodes (12): POST(), POST(), DELETE(), PATCH(), POST(), requireAdmin(), deleteFile(), getPublicUrl() (+4 more)
|
||||
|
||||
### Community 17 - "Module Page & Lesson Sorting"
|
||||
Cohesion: 0.23
|
||||
Nodes (11): createLesson(), deleteLesson(), moveLessonToModule(), reorderLessons(), requireAdmin(), toggleLessonPublished(), updateLesson(), Lesson (+3 more)
|
||||
|
||||
### Community 18 - "Cross-cutting Course Actions"
|
||||
Cohesion: 0.12
|
||||
Nodes (16): Course Detail Page, Create Course Action, Admin Dashboard, AdminLayout(), Create Lesson Action, Create Module Action, User Detail Page, Create User Action (+8 more)
|
||||
|
||||
### Community 19 - "Create-User Form"
|
||||
Cohesion: 0.18
|
||||
Nodes (6): CreateUserForm(), focusHandlers, inputStyle, ROLES, metadata, createUser()
|
||||
|
||||
### Community 20 - "Load Testing"
|
||||
Cohesion: 0.20
|
||||
Nodes (9): BROWSER_HEADERS, courseRes, dashRes, jar, lessonRes, LESSONS, loginRes, options (+1 more)
|
||||
|
||||
### Community 21 - "Admin Comments Moderation"
|
||||
Cohesion: 0.24
|
||||
Nodes (6): Comment, CommentsTable(), inputStyle, adminDeleteComment(), metadata, Props
|
||||
|
||||
### Community 22 - "Admin Questions Split View"
|
||||
Cohesion: 0.22
|
||||
Nodes (5): FileAttachment, Message, QuestionDetail, QuestionSplitView(), QuestionSummary
|
||||
|
||||
### Community 23 - "Bridge: Shared Libs (auth/prisma/email)"
|
||||
Cohesion: 0.22
|
||||
Nodes (9): AudioRecorder, Better Auth, grantAccess, Email Senders, EnrollmentManager, HomeworkSection, Prisma Client, getSettings (+1 more)
|
||||
|
||||
### Community 24 - "Module Actions Standalone"
|
||||
Cohesion: 0.46
|
||||
Nodes (7): createLesson(), deleteLesson(), moveLessonToModule(), reorderLessons(), requireAdmin(), toggleLessonPublished(), updateLesson()
|
||||
|
||||
### Community 25 - "Student Course Sidebar Layout"
|
||||
Cohesion: 0.29
|
||||
Nodes (5): Props, Course, CourseSidebar(), Lesson, Module
|
||||
|
||||
### Community 26 - "Question Thread UI"
|
||||
Cohesion: 0.29
|
||||
Nodes (4): FileAttachment, Message, QuestionThread(), QuestionThreadProps
|
||||
|
||||
### Community 31 - "Admin Homework Actions"
|
||||
Cohesion: 0.83
|
||||
Nodes (3): deleteHomework(), requireAdmin(), saveHomework()
|
||||
|
||||
### Community 33 - "Email→Homework→Questions Bridge"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): Questions API Route, Email Helpers (Resend), Homework Submission Actions
|
||||
|
||||
### Community 37 - "Lesson Actions + Prisma Bridge"
|
||||
Cohesion: 0.67
|
||||
Nodes (3): Prisma Client Singleton, Lesson Comment Actions, Lesson Progress Actions
|
||||
|
||||
## Knowledge Gaps
|
||||
- **278 isolated node(s):** `config`, `LESSONS`, `TEST_USER`, `BROWSER_HEADERS`, `options` (+273 more)
|
||||
These have ≤1 connection - possible missing edges or undocumented components.
|
||||
- **42 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
|
||||
|
||||
## Suggested Questions
|
||||
_Questions this graph is uniquely positioned to answer:_
|
||||
|
||||
- **Why does `dependencies` connect `Markdown→TipTap Import` to `Next.js Config`, `Cross-cutting Course Actions`?**
|
||||
_High betweenness centrality (0.128) - this node is a cross-community bridge._
|
||||
- **Why does `auth` connect `Auth/Session & Student Profile` to `Admin Categories & UI Library`, `Auth Flow & App Settings`, `Markdown→TipTap Import`, `Email Notifications & Q&A API`, `Student Lesson Player`, `Lesson Editor & Quiz Tools`, `Admin User Detail (Balance/Contact)`, `Course Tree & Module Sorting`, `CSV Import/Export`, `Curator Homework Review`, `Prisma Client & Dashboards`, `Admin Users List & Enroll`, `S3 Upload Routes`, `Module Page & Lesson Sorting`, `Admin Comments Moderation`, `Admin Questions Split View`, `Module Actions Standalone`, `Student Course Sidebar Layout`, `Admin/Curator Dashboards`, `Admin Homework Actions`, `Lesson Actions Lib`, `Student Comment Actions`?**
|
||||
_High betweenness centrality (0.121) - this node is a cross-community bridge._
|
||||
- **What connects `config`, `LESSONS`, `TEST_USER` to the rest of the system?**
|
||||
_278 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||
- **Should `Admin Categories & UI Library` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.06377151799687011 - nodes in this community are weakly interconnected._
|
||||
- **Should `Auth Flow & App Settings` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.06787330316742081 - nodes in this community are weakly interconnected._
|
||||
- **Should `Markdown→TipTap Import` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.05094130675526024 - nodes in this community are weakly interconnected._
|
||||
- **Should `Student Lesson Player` be split into smaller, more focused modules?**
|
||||
_Cohesion score 0.08235294117647059 - nodes in this community are weakly interconnected._
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "label": "logout-button.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L1"}, {"id": "layout_logout_button_logoutbutton", "label": "LogoutButton()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_client_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "target": "lib_auth_client_signout", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "target": "layout_logout_button_logoutbutton", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "layout_logout_button_logoutbutton", "callee": "useRouter", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/layout/logout-button.tsx", "source_location": "L7"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_prisma_config_ts", "label": "prisma.config.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/prisma.config.ts", "source_location": "L1"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_prisma_config_ts", "target": "config", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/prisma.config.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_prisma_config_ts", "target": "config", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/prisma.config.ts", "source_location": "L4", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L1"}, {"id": "import_export_page_metadata", "label": "metadata", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L5"}, {"id": "import_export_page_importexportpage", "label": "ImportExportPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L7"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_importer_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "admin_csv_importer_csvimporter", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "admin_csv_exporter_csvexporter", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "import_export_page_metadata", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_import_export_page_tsx", "target": "import_export_page_importexportpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L7", "weight": 1.0}], "raw_calls": [{"caller_nid": "import_export_page_importexportpage", "callee": "findMany", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/import-export/page.tsx", "source_location": "L8"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "label": "actions.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L1"}, {"id": "comments_actions_admindeletecomment", "label": "adminDeleteComment()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L8"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "cache", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_actions_ts", "target": "comments_actions_admindeletecomment", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L8", "weight": 1.0}], "raw_calls": [{"caller_nid": "comments_actions_admindeletecomment", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L9"}, {"caller_nid": "comments_actions_admindeletecomment", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L9"}, {"caller_nid": "comments_actions_admindeletecomment", "callee": "update", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L12"}, {"caller_nid": "comments_actions_admindeletecomment", "callee": "revalidatePath", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/actions.ts", "source_location": "L17"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "label": "homework-filters.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L1"}, {"id": "admin_homework_filters_inputstyle", "label": "inputStyle", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L7"}, {"id": "admin_homework_filters_course", "label": "Course", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L16"}, {"id": "admin_homework_filters_homeworkfilters", "label": "HomeworkFilters()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L18"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "lucide_react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "admin_homework_filters_inputstyle", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L7", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "admin_homework_filters_course", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L16", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_homework_filters_tsx", "target": "admin_homework_filters_homeworkfilters", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L18", "weight": 1.0}], "raw_calls": [{"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "useRouter", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L19"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "usePathname", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L20"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "useSearchParams", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L21"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "useTransition", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L22"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L32"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L33"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L34"}, {"caller_nid": "admin_homework_filters_homeworkfilters", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/homework-filters.tsx", "source_location": "L82"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_postcss_config_mjs", "label": "postcss.config.mjs", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/postcss.config.mjs", "source_location": "L1"}, {"id": "lms_system_postcss_config_config", "label": "config", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/postcss.config.mjs", "source_location": "L1"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_postcss_config_mjs", "target": "lms_system_postcss_config_config", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/postcss.config.mjs", "source_location": "L1", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "label": "register-form.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L1"}, {"id": "register_register_form_props", "label": "Props", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L7"}, {"id": "register_register_form_registerform", "label": "RegisterForm()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L14"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_client_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "lib_auth_client_signup", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "register_register_form_props", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L7", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "target": "register_register_form_registerform", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L14", "weight": 1.0}], "raw_calls": [{"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L15"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L16"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L17"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L18"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L19"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L20"}, {"caller_nid": "register_register_form_registerform", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L21"}, {"caller_nid": "register_register_form_registerform", "callee": "filter", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L33"}, {"caller_nid": "register_register_form_registerform", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/register-form.tsx", "source_location": "L136"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "label": "prisma.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L1"}, {"id": "lib_prisma_globalforprisma", "label": "globalForPrisma", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L4"}, {"id": "lib_prisma_createprismaclient", "label": "createPrismaClient()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L8"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "target": "users_dementiy_documents_claude_lms_system_src_generated_prisma_client_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "target": "prisma_client_prismaclient", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "target": "adapter_pg", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "target": "lib_prisma_globalforprisma", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "target": "lib_prisma_createprismaclient", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/prisma.ts", "source_location": "L8", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "label": "actions.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L1"}, {"id": "profile_actions_changepasswordaction", "label": "changePasswordAction()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L8"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "bcryptjs", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_profile_actions_ts", "target": "profile_actions_changepasswordaction", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L8", "weight": 1.0}], "raw_calls": [{"caller_nid": "profile_actions_changepasswordaction", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L9"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L10"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "get", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L11"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L17"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L17"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "findFirst", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L20"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "compare", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L25"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "hash", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L28"}, {"caller_nid": "profile_actions_changepasswordaction", "callee": "update", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/profile/actions.ts", "source_location": "L29"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_ui_textarea_tsx", "label": "textarea.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L1"}, {"id": "ui_textarea_textarea", "label": "Textarea()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L5"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_ui_textarea_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_textarea_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_utils_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_textarea_tsx", "target": "lib_utils_cn", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_textarea_tsx", "target": "ui_textarea_textarea", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L5", "weight": 1.0}], "raw_calls": [{"caller_nid": "ui_textarea_textarea", "callee": "cn", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/textarea.tsx", "source_location": "L9"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L1"}, {"id": "quizid_page_props", "label": "Props", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L5"}, {"id": "quizid_page_adminquizattemptspage", "label": "AdminQuizAttemptsPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L9"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "quizid_page_props", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_quizid_page_tsx", "target": "quizid_page_adminquizattemptspage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L9", "weight": 1.0}], "raw_calls": [{"caller_nid": "quizid_page_adminquizattemptspage", "callee": "findUnique", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L12"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "notFound", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L30"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L32"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "findMany", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L33"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "fromEntries", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L37"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L37"}, {"caller_nid": "quizid_page_adminquizattemptspage", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/[quizId]/page.tsx", "source_location": "L65"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L1"}, {"id": "settings_page_metadata", "label": "metadata", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L4"}, {"id": "settings_page_settingspage", "label": "SettingsPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_settings_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "lib_settings_getsettings", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_settings_form_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "admin_settings_form_settingsform", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "settings_page_metadata", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_settings_page_tsx", "target": "settings_page_settingspage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "settings_page_settingspage", "callee": "getSettings", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/settings/page.tsx", "source_location": "L7"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "label": "badge.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L1"}, {"id": "ui_badge_badgevariants", "label": "badgeVariants", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L7"}, {"id": "ui_badge_badge", "label": "Badge()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L30"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "merge_props", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "use_render", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "class_variance_authority", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_utils_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "lib_utils_cn", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "ui_badge_badgevariants", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L7", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_badge_tsx", "target": "ui_badge_badge", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L30", "weight": 1.0}, {"source": "ui_badge_badge", "target": "ui_badge_badgevariants", "relation": "calls", "context": "call", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L40", "weight": 1.0}], "raw_calls": [{"caller_nid": "ui_badge_badge", "callee": "useRender", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L36"}, {"caller_nid": "ui_badge_badge", "callee": "mergeProps", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L38"}, {"caller_nid": "ui_badge_badge", "callee": "cn", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/badge.tsx", "source_location": "L40"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L1"}, {"id": "login_page_loginpage", "label": "LoginPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L4"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_settings_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "target": "lib_settings_getsetting", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_app_auth_login_login_form_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "target": "login_login_form_loginform", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_login_page_tsx", "target": "login_page_loginpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L4", "weight": 1.0}], "raw_calls": [{"caller_nid": "login_page_loginpage", "callee": "all", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L9"}, {"caller_nid": "login_page_loginpage", "callee": "getSetting", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/login/page.tsx", "source_location": "L10"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "label": "admin-nav.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L1"}, {"id": "admin_admin_nav_links", "label": "links", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L6"}, {"id": "admin_admin_nav_adminnav", "label": "AdminNav()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L19"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "target": "admin_admin_nav_links", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "target": "admin_admin_nav_adminnav", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L19", "weight": 1.0}], "raw_calls": [{"caller_nid": "admin_admin_nav_adminnav", "callee": "usePathname", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L20"}, {"caller_nid": "admin_admin_nav_adminnav", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-nav.tsx", "source_location": "L24"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L1"}, {"id": "register_page_registerpage", "label": "RegisterPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L5"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_settings_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "lib_settings_getsettings", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_app_auth_register_register_form_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "register_register_form_registerform", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_register_page_tsx", "target": "register_page_registerpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L5", "weight": 1.0}], "raw_calls": [{"caller_nid": "register_page_registerpage", "callee": "getSettings", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L6"}, {"caller_nid": "register_page_registerpage", "callee": "redirect", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/register/page.tsx", "source_location": "L9"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_ui_label_tsx", "label": "label.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L1"}, {"id": "ui_label_label", "label": "Label()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L7"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_ui_label_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_label_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_utils_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_label_tsx", "target": "lib_utils_cn", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_label_tsx", "target": "ui_label_label", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L7", "weight": 1.0}], "raw_calls": [{"caller_nid": "ui_label_label", "callee": "cn", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/label.tsx", "source_location": "L11"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "label": "s3.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L1"}, {"id": "lib_s3_s3", "label": "s3", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L4"}, {"id": "lib_s3_getpublicurl", "label": "getPublicUrl()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L16"}, {"id": "lib_s3_uploadfile", "label": "uploadFile()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L20"}, {"id": "lib_s3_deletefile", "label": "deleteFile()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L36"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "client_s3", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "s3_request_presigner", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "lib_s3_s3", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "lib_s3_getpublicurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L16", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "lib_s3_uploadfile", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L20", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_lib_s3_ts", "target": "lib_s3_deletefile", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L36", "weight": 1.0}, {"source": "lib_s3_uploadfile", "target": "lib_s3_getpublicurl", "relation": "calls", "context": "call", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L33", "weight": 1.0}], "raw_calls": [{"caller_nid": "lib_s3_uploadfile", "callee": "send", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L25"}, {"caller_nid": "lib_s3_deletefile", "callee": "send", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/lib/s3.ts", "source_location": "L37"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "label": "admin-shell.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L1"}, {"id": "admin_admin_shell_adminshell", "label": "AdminShell()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L4"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_nav_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "target": "admin_admin_nav_adminnav", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_layout_logout_button_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "target": "layout_logout_button_logoutbutton", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "target": "admin_admin_shell_adminshell", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/admin-shell.tsx", "source_location": "L4", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_eslint_config_mjs", "label": "eslint.config.mjs", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L1"}, {"id": "lms_system_eslint_config_eslintconfig", "label": "eslintConfig", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L5"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_eslint_config_mjs", "target": "config", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_eslint_config_mjs", "target": "core_web_vitals", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_eslint_config_mjs", "target": "typescript", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_eslint_config_mjs", "target": "lms_system_eslint_config_eslintconfig", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/eslint.config.mjs", "source_location": "L5", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "label": "delete-action.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L1"}, {"id": "comments_delete_action_deletecomment", "label": "deleteComment()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L8"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "cache", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_comments_delete_action_ts", "target": "comments_delete_action_deletecomment", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L8", "weight": 1.0}], "raw_calls": [{"caller_nid": "comments_delete_action_deletecomment", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L9"}, {"caller_nid": "comments_delete_action_deletecomment", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L9"}, {"caller_nid": "comments_delete_action_deletecomment", "callee": "update", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L12"}, {"caller_nid": "comments_delete_action_deletecomment", "callee": "revalidatePath", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/comments/delete-action.ts", "source_location": "L17"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "label": "button.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L1"}, {"id": "ui_button_buttonvariants", "label": "buttonVariants", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L6"}, {"id": "ui_button_button", "label": "Button()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L43"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "button", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "class_variance_authority", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_utils_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "lib_utils_cn", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "ui_button_buttonvariants", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_button_tsx", "target": "ui_button_button", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L43", "weight": 1.0}, {"source": "ui_button_button", "target": "ui_button_buttonvariants", "relation": "calls", "context": "call", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L52", "weight": 1.0}], "raw_calls": [{"caller_nid": "ui_button_button", "callee": "cn", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/button.tsx", "source_location": "L52"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_entrypoint_sh", "label": "entrypoint.sh", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/entrypoint.sh", "source_location": "L1"}], "edges": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L1"}, {"id": "forgot_password_page_forgotpasswordpage", "label": "ForgotPasswordPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L4"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_settings_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "target": "lib_settings_getsetting", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_forgot_password_form_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "target": "forgot_password_forgot_password_form_forgotpasswordform", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_forgot_password_page_tsx", "target": "forgot_password_page_forgotpasswordpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L4", "weight": 1.0}], "raw_calls": [{"caller_nid": "forgot_password_page_forgotpasswordpage", "callee": "getSetting", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/forgot-password/page.tsx", "source_location": "L5"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "label": "input.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L1"}, {"id": "ui_input_input", "label": "Input()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "target": "input", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_utils_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "target": "lib_utils_cn", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_ui_input_tsx", "target": "ui_input_input", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "ui_input_input", "callee": "cn", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/ui/input.tsx", "source_location": "L11"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L1"}, {"id": "questions_page_adminquestionspage", "label": "AdminQuestionsPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_questions_questionsplitview_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "questions_questionsplitview_questionsplitview", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_questions_page_tsx", "target": "questions_page_adminquestionspage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "questions_page_adminquestionspage", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L7"}, {"caller_nid": "questions_page_adminquestionspage", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L7"}, {"caller_nid": "questions_page_adminquestionspage", "callee": "redirect", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/questions/page.tsx", "source_location": "L8"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_auth_verify_email_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/verify-email/page.tsx", "source_location": "L1"}, {"id": "verify_email_page_verifyemailpage", "label": "VerifyEmailPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/verify-email/page.tsx", "source_location": "L3"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_auth_verify_email_page_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/verify-email/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_auth_verify_email_page_tsx", "target": "verify_email_page_verifyemailpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(auth)/verify-email/page.tsx", "source_location": "L3", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "label": "layout.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L1"}, {"id": "admin_layout_adminlayout", "label": "AdminLayout()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L7"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_admin_shell_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "admin_admin_shell_adminshell", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_layout_tsx", "target": "admin_layout_adminlayout", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L7", "weight": 1.0}], "raw_calls": [{"caller_nid": "admin_layout_adminlayout", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L8"}, {"caller_nid": "admin_layout_adminlayout", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L8"}, {"caller_nid": "admin_layout_adminlayout", "callee": "redirect", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L9"}, {"caller_nid": "admin_layout_adminlayout", "callee": "redirect", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L10"}, {"caller_nid": "admin_layout_adminlayout", "callee": "count", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/layout.tsx", "source_location": "L12"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L1"}, {"id": "new_page_metadata", "label": "metadata", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L4"}, {"id": "new_page_newuserpage", "label": "NewUserPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_components_admin_create_user_form_tsx", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "target": "admin_create_user_form_createuserform", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "target": "new_page_metadata", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_users_new_page_tsx", "target": "new_page_newuserpage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/users/new/page.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": []}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "label": "route.ts", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L1"}, {"id": "close_route_patch", "label": "PATCH()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "server", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "headers", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_auth_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "lib_auth_auth", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_api_questions_id_close_route_ts", "target": "close_route_patch", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "close_route_patch", "callee": "getSession", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L10"}, {"caller_nid": "close_route_patch", "callee": "headers", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L10"}, {"caller_nid": "close_route_patch", "callee": "json", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L11"}, {"caller_nid": "close_route_patch", "callee": "json", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L14"}, {"caller_nid": "close_route_patch", "callee": "findUnique", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L19"}, {"caller_nid": "close_route_patch", "callee": "json", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L20"}, {"caller_nid": "close_route_patch", "callee": "json", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L22"}, {"caller_nid": "close_route_patch", "callee": "update", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L25"}, {"caller_nid": "close_route_patch", "callee": "json", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/api/questions/[id]/close/route.ts", "source_location": "L30"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L1"}, {"id": "quizzes_page_metadata", "label": "metadata", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L4"}, {"id": "quizzes_page_adminquizzespage", "label": "AdminQuizzesPage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L6"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "target": "link", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "target": "quizzes_page_metadata", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_admin_quizzes_page_tsx", "target": "quizzes_page_adminquizzespage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "quizzes_page_adminquizzespage", "callee": "findMany", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L7"}, {"caller_nid": "quizzes_page_adminquizzespage", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/admin/quizzes/page.tsx", "source_location": "L44"}]}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "label": "page.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L1"}, {"id": "slug_page_props", "label": "Props", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L5"}, {"id": "slug_page_coursepage", "label": "CoursePage()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L9"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "users_dementiy_documents_claude_lms_system_src_lib_prisma_ts", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "lib_prisma_prisma", "relation": "imports", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L1", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L2", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "navigation", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "slug_page_props", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L5", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_app_student_courses_slug_page_tsx", "target": "slug_page_coursepage", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L9", "weight": 1.0}], "raw_calls": [{"caller_nid": "slug_page_coursepage", "callee": "findUnique", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L12"}, {"caller_nid": "slug_page_coursepage", "callee": "notFound", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L29"}, {"caller_nid": "slug_page_coursepage", "callee": "redirect", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/app/(student)/courses/[slug]/page.tsx", "source_location": "L34"}]}
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "label": "csv-exporter.tsx", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L1"}, {"id": "admin_csv_exporter_course", "label": "Course", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L6"}, {"id": "admin_csv_exporter_inputstyle", "label": "inputStyle", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L8"}, {"id": "admin_csv_exporter_focushandlers", "label": "focusHandlers", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L17"}, {"id": "admin_csv_exporter_csvexporter", "label": "CsvExporter()", "file_type": "code", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L24"}], "edges": [{"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L3", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "lucide_react", "relation": "imports_from", "context": "import", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L4", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "admin_csv_exporter_course", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L6", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "admin_csv_exporter_inputstyle", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L8", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "admin_csv_exporter_focushandlers", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L17", "weight": 1.0}, {"source": "users_dementiy_documents_claude_lms_system_src_components_admin_csv_exporter_tsx", "target": "admin_csv_exporter_csvexporter", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L24", "weight": 1.0}], "raw_calls": [{"caller_nid": "admin_csv_exporter_csvexporter", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L25"}, {"caller_nid": "admin_csv_exporter_csvexporter", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L26"}, {"caller_nid": "admin_csv_exporter_csvexporter", "callee": "useState", "is_member_call": false, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L27"}, {"caller_nid": "admin_csv_exporter_csvexporter", "callee": "map", "is_member_call": true, "source_file": "/Users/dementiy/Documents/Claude/lms-system/src/components/admin/csv-exporter.tsx", "source_location": "L60"}]}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user