Compare commits
147 Commits
v1.0
...
980c4d621e
| Author | SHA1 | Date | |
|---|---|---|---|
| 980c4d621e | |||
| 8c2bd42be3 | |||
| fa7c25465c | |||
| 1133557a62 | |||
| a6835567f1 | |||
| c2f84079ac | |||
| f8f246ca9b | |||
| d2094c9541 | |||
| 7c8e72cd94 | |||
| d20e277535 | |||
| 47e301a5b8 | |||
| 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
|
||||
+3
-3
@@ -1,15 +1,15 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
image: postgres:18-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: lms_user
|
||||
POSTGRES_PASSWORD: lms_password
|
||||
POSTGRES_DB: lms_db
|
||||
ports:
|
||||
- "5432:5432"
|
||||
- "127.0.0.1:5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- postgres_data:/var/lib/postgresql
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
@@ -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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user