media-gallery

Media Gallery

HR bot (Telegram, StackHiringBot) — медіатека для HR/Recruiter: завантаження й модерація файлів (фото/відео/pdf/docs) з іменем і описом, які згодом відправляються кандидатам HR-бота як шаблони в Telegram. За задумом — аналог сусіднього модуля emails/email-media (той самий upload → moderation → status → send), але без прив’язки до “леді” (тут прив’язки до сутності взагалі немає — плоский спільний список HR+Recruiter).

Документ — аналіз задачі до реалізації. Верифіковані факти — у розділах нижче; невідоме — у “Відкриті питання” вверху, на явний запит.


Відкриті питання

1. Відправка в Telegram файлу, що зберігається по URL (uploadFile)

З’ясовано аналізом коду:

  • FileManagerService.uploadFile (src/stack/components/FileManger/FileManagerService.ts) заливає файл у Cloudflare R2 (S3-сумісний) і повертає публічний URL (${R2_PUBLIC_URL}/${key}) — те саме джерело, яким користується email-media (fileUrl в email-media.service.ts).
  • HR bot вже вміє відправляти вкладення просто по URL, без проміжного завантаження на свій бек: hiring-telegraf-bot.service.ts → _handleStandardMessage викликає this.bot.telegram.sendPhoto/sendDocument/sendAudio/sendVoice(telegramId, attachment.url, extra) — Telegraf/Telegram Bot API приймає HTTP(S)-посилання як source і сам його стягує на своїй стороні. Тобто для Media Gallery принципово нової механіки не треба — той самий підхід підходить і для шаблонів.
  • Проблема, яку треба закрити: у поточному switch немає кейсу video (sendVideo) — є тільки photo | document | audio | voice. Задача явно вимагає тип “відео”, тому кейс треба додати.
  • Відкрито (потребує перевірки на реальних файлах): Telegram Bot API обмежує розмір файлу, який приймає саме по URL (менше, ніж при прямому мультипарт-аплоаді на sendX). Для відео/pdf з рекрутингу файли можуть бути більшими — треба перевірити на практиці, чи всі кандидатські медіа проходять по URL, чи для важких файлів знадобиться прогрузка через multipart (тобто спершу скачати з R2 на бек, потім віддати в Telegram як buffer/stream).
  • Відкрито: чи pdf/docs взагалі коректно рендеряться Telegram-клієнтом як sendDocument по URL (технічно так, Telegram сам віддає файл як документ) — здається безпроблемним, але не перевірено на цьому боті.

2. Доступи HR / Recruiter (чернетка від задачі, потребує підтвердження)

ДіяHRRecruiter
Завантажити медіа✓ — тільки в статус on_moderation✓ — тільки в статус on_moderation
Модерація (approve/reject)
Авто-видалення rejected 3 дня з коментарем
Редагування (ім’я/опис)своїх on_moderation
  • Це буквально дзеркалить email-media: там topManager/supervisor = повний доступ (create+moderate+delete), operator = тільки create → on_moderation. HR ~ supervisor/topManager, Recruiter ~ operator.
  • Відкрито: Recruiter бачить весь список медіа.
  • Відкрито: Для Media Gallery йти шляхом - декларативний MiddlevareGuard([...roles])

3. На майбутнє: прив’язка медіа до команди

  • Наразі (як і в чернетці задачі) немає сутності “команда” для HR/Recruiter в контексті медіа — це буде плоский спільний список на всіх HR+Recruiter.
  • Питання на майбутнє (не блокує MVP): чи прив’язувати медіа до hrId-власника (recruiter бачить тільки галерею “свого” HR), чи вводити окрему сутність команди. Поки що рішення відкладено — фіксуємо тут, щоб не загубилось.

Суть

Спільна медіатека для HR і Recruiter HR-бота (Telegram, user/StackHiringBot): завантаження файлів (фото, відео, pdf, docs) з ім’ям і описом, модерація перед використанням, і подальша відправка як шаблон повідомлення кандидату в Telegram.

Дизайн за аналогією з Email Media

Email Media (сусідній модуль, є)Media Gallery (HR bot, план)
Прив’язкадо stackLadyId (конкретна анкета)без прив’язки до конкретної сутності (спільний список) — див. “Відкрите питання 3”
Типиphoto, video, audiophoto, video, pdf, docs (== “документ”)
Статусиapproved / rejected / on_moderationтой самий набір, за задачею
Ім’я/описname (з файлу), опису немаname — можна вказати вручну; новий description
Хто завантажує з фінальним статусомtopManager/supervisorHR
Хто завантажує тільки в модераціюoperatorRecruiter
Видалення файлувидаляє і оригінал, і previewUrl (прев’ю з відео) з R2той самий підхід, якщо буде preview для відео
Використанняприкріплюється до email через emailMediaIdsвідправляється як шаблон в Telegram кандидату HR bot

Що інакше в типах медіа

Поточний EmailMediaType = photo | video | audio (без документів). Для Media Gallery знадобиться новий enum/мапа з підтримкою pdf, docs.

Структура файлів (план)

Бек — папка media-gallery/ розміщена всередині user/StackHiringBot/ (не поруч з emails/) — бо це не крос-фічева бібліотека, а частина домену одного конкретного бота (HR/Recruiter, один hiring-bot). Внутрішня структура папки один-в-один копіює скелет email-media, тільки без stackLadyId і з новими полями/типами.

user/StackHiringBot/media-gallery/
├── dto/
│   └── index.ts                 # GetMediaGalleryDto, MediaGalleryIdDto, UpdateMediaGalleryStatusDto, UpdateMediaGalleryInfoDto
├── media-gallery.model.ts       # MediaGallery, MediaGalleryStatus (approved/rejected/on_moderation), MediaGalleryType (photo/video/pdf/docs)
├── media-gallery.repository.ts # getMedia (cursor+filters), createMedia, findById, deleteById, updateStatus, updateInfo (name/description)
├── media-gallery.service.ts    # роль → стартовий статус (HR=approved, Recruiter=on_moderation), правила видалення/редагування, прев'ю відео
├── media-gallery.controller.ts # MiddlevareGuard([StackRoles.hr, StackRoles.recruiter]) — за рішенням у "Відкриті питання → 2"
└── utils.ts                    # MIME_TYPE_MAP (+ pdf/docs), convertUserToMediaGalleryUserInfoResponse, checkMediaGalleryEditable(media, user)

Точки дотику в існуючому коді (не нові файли, а правки):

  • src/TYPES.ts — символи MediaGalleryController / MediaGalleryService / MediaGalleryRepository, за зразком EmailMedia*.
  • src/stack/StackModule.ts — імпорт з ./components/user/StackHiringBot/media-gallery/media-gallery.controller, bind(...).inSingletonScope(), this.app.use('/media-gallery', ...).
  • user/StackHiringBot/interfaces.tsStandardMessagePayload.attachment.type розширити з 'photo' | 'document' | 'voice' | 'audio' до + 'video'.
  • user/StackHiringBot/hiring-telegraf-bot.service.ts (_handleStandardMessage) — додати case 'video': sendVideo(...).
  • user/StackHiringBot/hiring-workspace.service.ts + hiring-workspace.controller.ts — новий метод/route sendMediaTemplate (hireId + mediaGalleryId, без реаплоаду файлу — бере готовий url/type з галереї, на відміну від sendMessage, який завжди приймає свіжий файл).

UI (репо stack-client) — за зразком переюзаного common_stack_components/email_media, але без page_users (нема дерева supervisor→operator→lady — див. “Відкрите питання 3”: список поки що плоский):

stack-client/src/components/common_stack_components/media_gallery/
├── Index.tsx                 # role-обгортка (hr | recruiter), allowedActionsByRole — як в email_media
├── center_section/
│   └── Index.tsx             # список approved/rejected + upload + фільтри по типу
├── moderation_section/
│   └── Index.tsx             # черга on_moderation — approve/reject (HR), перегляд своїх (Recruiter)
├── types.ts                  # MediaGalleryType, MediaGalleryStatus, MediaFile, MediaGalleryAllowedActions
└── utils/
    └── useLoadMediaGallery.ts

Плюс:

  • stack-client/src/utils/mediaGallery.js — константи ендпоінтів (MEDIA_GALLERY_GET_BY_OPTIONS, _CREATE_FILE, _DELETE_FILE, _UPDATE_STATUS, _UPDATE_INFO), за зразком utils/emailMedia.js.
  • Точка входу сторінки — новий пункт в лівому меню HR/Recruiter, ймовірно src/components/recruiter/media_gallery/Index.tsx, що рендерить <MediaGallery role={...} /> (конкретне місце в меню/роутингу — TBD, не досліджено).
  • Відправка шаблону з чату кандидата — picker в footer workspace/live_chat (за зразком golden emails_footer/media/MediaPicker.tsx) — конкретний файл/шлях TBD.

Зв’язки

  • Аналог: emails/email-media (src/stack/components/emails/email-media/, в тому ж репо, але інший бounded-context) — той самий патерн upload → moderation → status → send.
  • Домен: user/StackHiringBot/ — сусідні файли hiring-workspace.*, hiring-telegraf-bot.service.ts тощо; media-gallery/ — єдина папка-виняток серед переважно пласких hiring-* файлів цієї директорії.
  • Telegram-відправка: hiring-telegraf-bot.service.ts (_handleStandardMessage) — точка, куди додається кейс video і шаблонна відправка медіа-галереї.
  • UI: common_stack_components/email_media (репо stack-client, той самий компонент з рольовими allowedActions вже параметризований по role/family) — джерело патерну для media_gallery.

Сторінки

  • UI — TBD, план шляху: stack-client/src/components/common_stack_components/media_gallery/ (див. “Структура файлів (план)”)
  • Backend — TBD, план шляху: user/StackHiringBot/media-gallery/ (див. “Структура файлів (план)“)