feat(subscriptions): add personal AI subscriptions page #9
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/subscriptions-page"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что сделано
Реализована статичная страница
/subscriptions— личная AI-подписки (issue #7).Новые файлы
src/lib/subscriptions.ts— конфиг 4 сервисов (имя + URL)src/app/[locale]/subscriptions/page.tsx— статичная страница (SSG), 4 карточки + сноска + JSON-LDsrc/__tests__/subscriptions-metadata.test.ts— 6 тестов metadatasrc/__tests__/subscriptions.test.ts— 7 тестов конфига + словарейsrc/__tests__/subscriptions-page.test.tsx— 2 теста footer-ссылкиИзменённые файлы
src/lib/i18n.ts— секцияsubscriptionsв типеDict+ruDict/enDict, полеfooter.subscriptionssrc/components/site-footer.tsx— текстовая ссылкаnext/link(междуmadeByи heart)src/app/sitemap.ts— роут/subscriptions(priority 0.6)biome.json— overridenoDangerouslySetInnerHtmloff для JSON-LDКарточки
Имена брендов не локализуются (только в конфиге), описания — в i18n (ru/en). Внешние ссылки
target="_blank" rel="noopener noreferrer". Footer-ссылка черезnext/link(client-side nav).Почему
SEO-трафик + реферальные ссылки. Контент стабильный (только личный опыт и механика лимитов, без конкретных цифр/сумм). Изменчивость покрыта сноской со звёздочкой. Страница полностью статическая (SSG), как
/contacts— никакихcookies()/headers()/fetch к внешним API.Watch out
src/app/[locale]/subscriptions/page.tsxвoverrides[0].include.contacts.test.tsxпроверяетsvgs[0](heart) иsvgs[1](forgejo). Новая ссылка текстовая (без SVG) — индексы не сбились, тест проходит.exactOptionalPropertyTypes: карточки описаний черезcards[i]?.description ?? ""(noUncheckedIndexedAccess guard).Pending
Closes #7
Code Review Summary
Реализована статичная страница
/subscriptions(issue #7) — 4 карточки AI-подписок + сноска + JSON-LD + footer-ссылка + sitemap-роут. Код следует паттерну/contacts(SSG, тот же стиль metadata/JSON-LD), тесты покрывают конфиг/словари/metadata/footer. Все контракты ТЗ выполнены verbatim.Positives
src/lib/i18n.ts:170,174,178,182). EN — адаптированный перевод (как и требует ТЗ: «EN — адаптированный перевод»).?referral=KKPRqRfw(Synthetic.new),?ref=YZ78E3D7(Umans) —src/lib/subscriptions.ts:8,10.src/lib/i18n.ts:166).next/link(НЕ<a>), внутренняя (безtarget), классtext-muted transition hover:text-accent, текст «Подписки»/«Subscriptions» через i18n, расположена рядом сmadeBy—src/components/site-footer.tsx:11-13. Шапку НЕ трогали.target="_blank" rel="noopener noreferrer"на обеих ссылках карточки (заголовок + кнопка) —src/app/[locale]/subscriptions/page.tsx:60-61,69-70.@id/url=${baseUrl}/${loc}/subscriptions,inLanguageчерезjsonLdLanguage(loc)—page.tsx:40,43,44./${loc}/subscriptions, OG title с суффиксом// slaid098.dev, OG locale черезogLocale(loc)—page.tsx:18,20,23.cookies()/headers()/fetch к внешним API/export const revalidate. Страница SSG (layout имеетgenerateStaticParamsдля locale).src/app/[locale]/subscriptions/page.tsx(noDangerouslySetInnerHtml: offдля JSON-LD) —biome.json:42.noUncheckedIndexedAccessguardcard?.description ?? ""(page.tsx:55),exactOptionalPropertyTypesсоблюдён.contacts.test.tsx:142-143проверяетsvgs[0](heart) иsvgs[1](forgejo). Новая subscriptions-ссылка текстовая (черезnext/link, без SVG) → SVG-индексы не сбились.SUBSCRIPTIONSиспользуется в page + tests; секцияsubscriptionsi18n используется в page + tests. Мёртвого кода нет.src/lib/subscriptions.ts(отдельный файл,readonlyмассив), страница следует существующей структуре роутов, тесты вsrc/__tests__/. Файлы < 300 строк, функции < 30 строк (компонент 57 строк — JSX render, нормально).## Что сделано/## Почему/## Watch out/## Pendingзаполнены осмысленно,Closes #7.Suggestions (info, not blocking)
const card = d.subscriptions.cards[i]прямой индексный доступ. Guardcard?.description ?? ""на строке 55 корректно обрабатываетT | undefined(noUncheckedIndexedAccess). Можно было бы использоватьSUBSCRIPTIONS[i]+cards.find, но текущий подход соответствует паттерну и работает — менять не нужно.key={sub.url}— URL как React key. Уникальны и стабильны — корректный выбор.Контракты ТЗ выполнены. CI на PR не запустился (проблема триггера ci.yml — отдельная проблема, не блокер review). Локально biome/tsc/vitest/build зелёные (153 теста).
Verdict: APPROVE