Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/docker-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
- name: Set matrix
id: set-matrix
run: |
APPS=("web-app" "web-storefront" "web-parser" "atrium-telegram" "storefront-telegram" "core-telegram")
APPS=("web-app" "web-storefront" "web-parser" "atrium-telegram" "storefront-telegram" "core-telegram" "webinar")
CHANGED=()

if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Set package in env
id: set-package
run: |
APPS=("web-app" "web-storefront" "web-parser" "atrium-telegram" "storefront-telegram" "core-telegram")
APPS=("web-app" "web-storefront" "web-parser" "atrium-telegram" "storefront-telegram" "core-telegram" "webinar")
MATCH="${{ steps.regex-match.outputs.match }}"

if [ -z "$MATCH" ]; then
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@
"sonarlint.connectedMode.project": {
"connectionId": "sushi-love",
"projectKey": "sushi-love_roll-stack"
}
},
"findUnusedExports.detectCircularImports": true
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const state = ref<Partial<CreateFlowItem>>({
title: undefined,
description: undefined,
type: 'user_post',
userId: userStore.id,
})

async function onSubmit(event: FormSubmitEvent<CreateFlowItem>) {
Expand Down
2 changes: 1 addition & 1 deletion apps/atrium-telegram/server/api/flow/index.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineEventHandler(async (event) => {
type: data.type,
title: data.title,
description: data.description,
userId: data.userId,
userId: event.context.user.id,
})
if (!item) {
throw createError({
Expand Down
1 change: 0 additions & 1 deletion apps/atrium-telegram/shared/services/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ export const createFlowItemSchema = type({
title: type('2 <= string <= 150').describe('error.length.invalid'),
description: type('string <= 1500 | undefined').describe('error.length.invalid').optional(),
type: flowTypeSchema.describe('error.length.invalid'),
userId: type('string | undefined').describe('error.length.invalid').optional(),
})
export type CreateFlowItem = typeof createFlowItemSchema.infer
60 changes: 60 additions & 0 deletions apps/webinar/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export default defineAppConfig({
ui: {
button: {
slots: {
base: 'font-semibold',
},
variants: {
size: {
xl: {
base: 'px-4 py-3 font-semibold',
},
},
variant: {
gradient: 'text-white bg-linear-to-br from-secondary-400 to-secondary-500 hover:opacity-90 disabled:from-neutral-300 disabled:to-neutral-400 aria-disabled:from-neutral-300 aria-disabled:to-neutral-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',
},
color: {
secondary: '!text-white disabled:!bg-inverted/25',
},
},
},
tabs: {
variants: {
variant: {
pill: {
trigger: 'data-[state=active]:!text-white',
},
gradient: {
list: 'bg-elevated rounded-lg',
trigger: 'data-[state=active]:bg-linear-to-br from-lime-300 to-lime-500 data-[state=active]:text-neutral-950 flex-1 w-full',
indicator: 'rounded-md shadow-xs',
},
},
},
},
modal: {
slots: {
content: 'divide-y-0',
header: 'pb-0 min-h-12',
title: 'text-lg/5 font-semibold',
},
},
navigationMenu: {
slots: {
link: 'text-sm',
},
},
toast: {
slots: {
title: 'text-lg/6',
description: 'leading-4',
icon: 'shrink-0 size-7',
},
},
card: {
slots: {
body: 'p-4 sm:p-4',
},
},
},
})
25 changes: 25 additions & 0 deletions apps/webinar/app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<UApp
:locale="locales[locale]"
class="min-h-svh"
>
<NuxtLoadingIndicator :color="false" class="bg-primary h-[2px]" />
<NuxtPage />
</UApp>
</template>

<script setup lang="ts">
import * as locales from '@nuxt/ui/locale'

const { locale } = useI18n()

const lang = computed(() => locales[locale.value].code)
const dir = computed(() => locales[locale.value].dir)

useHead({
htmlAttrs: {
lang,
dir,
},
})
</script>
3 changes: 3 additions & 0 deletions apps/webinar/app/assets/css/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-headers);
}
7 changes: 7 additions & 0 deletions apps/webinar/app/components/HeaderLogo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<img
src="/sushi-main-logo.png"
alt=""
class="mx-auto h-20 w-auto motion-preset-pop"
>
Comment on lines +2 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Logo needs accessible alt text; consider linking to home

Empty alt hides a meaningful image from screen readers; header logos should be descriptive. Also, wrapping with NuxtLink improves UX.

-  <img
-    src="/sushi-main-logo.png"
-    alt=""
-    class="mx-auto h-20 w-auto motion-preset-pop"
-  >
+  <NuxtLink to="/" aria-label="Home">
+    <img
+      src="/sushi-main-logo.png"
+      alt="Webinar logo"
+      class="mx-auto h-20 w-auto motion-preset-pop"
+    >
+  </NuxtLink>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<img
src="/sushi-main-logo.png"
alt=""
class="mx-auto h-20 w-auto motion-preset-pop"
>
<NuxtLink to="/" aria-label="Home">
<img
src="/sushi-main-logo.png"
alt="Webinar logo"
class="mx-auto h-20 w-auto motion-preset-pop"
>
</NuxtLink>
🤖 Prompt for AI Agents
In apps/webinar/app/components/HeaderLogo.vue around lines 2 to 6, the <img>
currently has an empty alt and is not a link; replace it with a NuxtLink to the
site root that wraps the image, and give the image a meaningful alt that
describes the logo and destination (e.g., alt="Sushi — Home"); preserve existing
classes and motion presets, do not set aria-hidden on the image, and ensure the
NuxtLink points to "/" so clicking the logo returns users to the homepage.

</template>
34 changes: 34 additions & 0 deletions apps/webinar/app/components/InfiniteTitlesDivider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<div class="bg-[#ffed00]">
<UCarousel
v-slot="{ item }"
loop
:auto-scroll="{
speed: 1,
}"
:items="items"
:ui="{
item: 'shrink-0 basis-full md:basis-1/3 min-w-120 h-full min-h-16 flex flex-row gap-4 items-center justify-around',
}"
>
<h4 class="text-2xl font-black uppercase">
{{ item }}
</h4>

<img
src="/sushi-heart.svg"
alt=""
class="w-10 h-auto opacity-15"
>
</UCarousel>
</div>
</template>

<script setup lang="ts">
const items = [
'Вебинар Суши Love 2025',
'Будем делиться опытом',
'Как избежать ошибок',
'На 100% бесплатно',
]
</script>
28 changes: 28 additions & 0 deletions apps/webinar/app/error.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<div class="flex flex-col max-w-sm mx-auto mt-32 text-center items-center">
<h1 class="text-4xl font-bold mb-4">
{{ $t('error.title') }} {{ error?.statusCode }}
</h1>
<p>{{ error?.statusMessage }}</p>

Comment on lines +3 to +7
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing i18n keys; add safe fallback for statusMessage

error.title and common.to-home aren’t defined in ru-RU.json. Also guard empty statusMessage.

-    <h1 class="text-4xl font-bold mb-4">
-      {{ $t('error.title') }} {{ error?.statusCode }}
-    </h1>
-    <p>{{ error?.statusMessage }}</p>
+    <h1 class="text-4xl font-bold mb-4">
+      {{ $t('error.title') }} {{ error?.statusCode }}
+    </h1>
+    <p v-if="error?.statusMessage">{{ error.statusMessage }}</p>
+    <p v-else>{{ $t('error.common') }}</p>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h1 class="text-4xl font-bold mb-4">
{{ $t('error.title') }} {{ error?.statusCode }}
</h1>
<p>{{ error?.statusMessage }}</p>
<h1 class="text-4xl font-bold mb-4">
{{ $t('error.title') }} {{ error?.statusCode }}
</h1>
<p v-if="error?.statusMessage">{{ error.statusMessage }}</p>
<p v-else>{{ $t('error.common') }}</p>
🤖 Prompt for AI Agents
In apps/webinar/app/error.vue around lines 3 to 7, the template uses i18n keys
that are missing in ru-RU.json and directly renders error.statusMessage without
a guard; add the missing keys (error.title and common.to-home) to ru-RU.json,
and update the template to use translation fallbacks for error.title and
common.to-home and guard/display a safe default for statusMessage when empty
(e.g., show a translated 'unknown error' or a static fallback string).

<UButton
variant="solid"
size="xl"
class="mt-12 w-full justify-center"
@click="handleError"
>
{{ $t('common.to-home') }}
</UButton>
</div>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</template>

<script setup lang="ts">
const { error } = defineProps<{ error: {
statusCode: number
statusMessage?: string
} }>()

function handleError() {
clearError({ redirect: '/' })
}
</script>
126 changes: 126 additions & 0 deletions apps/webinar/app/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<template>
<div class="mt-10">
<HeaderLogo />
</div>

<UPageHero
orientation="horizontal"
title="Мечтаете о своем заведении? Готовый алгоритм действий для новичков"
description="Бесплатный вебинар: «Как открыть ресторан, кофейню или суши-бар с нуля и избежать ошибок». Получите пошаговую инструкцию и ответы на все вопросы, от поиска помещения до первого заработка."
headline="Поторопись!"
:links="links"
:ui="{
headline: 'py-2 px-4 w-fit bg-secondary text-white rounded-lg motion-preset-seesaw',
}"
>
<video
controls
class="max-h-160 w-auto mx-auto rounded-xl"
>
<source src="https://storage.yandexcloud.net/sushi-love-test/telegram/files/BAACAgIAAxkBAAOFaMwda7U9ddiws5ZJ4oIbaeSlw0YAAvWDAAJt32BKU94XDWZ9cCg2BA.mp4" type="video/mp4">
Ваш браузер не поддерживает видео
</video>
</UPageHero>

<InfiniteTitlesDivider />

<UPageSection
title="Для кого этот вебинар?"
description="Этот вебинар идеально подойдет для вас, если вы:"
orientation="horizontal"
:reverse="true"
:features="firstSectionItems"
>
Тут фото?
</UPageSection>

<UPageSection
title="Что вы узнаете на вебинаре?"
description="За 1,5 часа вы получите готовый план действий"
orientation="horizontal"
:features="secondSectionItems"
>
Тут фото?
</UPageSection>
</template>

<script setup lang="ts">
import type { PageFeatureProps } from '@nuxt/ui'

const links = ref([
{
label: 'Записаться',
to: 'https://t.me/SLFranchiseBot',
target: '_blank',
trailingIcon: 'i-lucide-arrow-right',
ui: {
base: 'px-6 text-xl',
},
},
])
Comment on lines +50 to +60
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

External link opens in new tab without rel=noopener — fix reverse tabnabbing and simplify reactivity.

Add rel to external _blank link and avoid an unnecessary ref here.

Apply this diff:

-const links = ref([
+const links = [
   {
     label: 'Записаться',
     to: 'https://t.me/SLFranchiseBot',
     target: '_blank',
+    rel: 'noopener noreferrer',
     trailingIcon: 'i-lucide-arrow-right',
     ui: {
       base: 'px-6 text-xl',
     },
   },
-])
+]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const links = ref([
{
label: 'Записаться',
to: 'https://t.me/SLFranchiseBot',
target: '_blank',
trailingIcon: 'i-lucide-arrow-right',
ui: {
base: 'px-6 text-xl',
},
},
])
const links = [
{
label: 'Записаться',
to: 'https://t.me/SLFranchiseBot',
target: '_blank',
rel: 'noopener noreferrer',
trailingIcon: 'i-lucide-arrow-right',
ui: {
base: 'px-6 text-xl',
},
},
];
🤖 Prompt for AI Agents
In apps/webinar/app/pages/index.vue around lines 50 to 60, the external link
opens with target: '_blank' but lacks rel: 'noopener' (risk of reverse
tabnabbing) and the links array is unnecessarily wrapped in a ref; change the
links declaration from a ref to a plain constant (no reactive ref) and add rel:
'noopener' to the link object when target is '_blank' so the entry becomes a
plain array with the same fields plus rel: 'noopener'.


const firstSectionItems: PageFeatureProps[] = [
{
title: 'Мечтаете открыть свое заведение',
icon: 'i-lucide-store',
},
{
title: 'Ищете бизнес с быстрой окупаемостью',
icon: 'i-lucide-piggy-bank',
},
{
title: 'Не знаете, с чего начать и сколько денег нужно',
icon: 'i-lucide-chart-pie',
},
{
title: 'Боитесь не справиться с конкуренцией и привлечением гостей',
icon: 'i-lucide-users',
},
{
title: 'Не понимаете, как контролировать закупки, себестоимость и воровство на кухне',
icon: 'i-lucide-hand-coins',
},
]

const secondSectionItems: PageFeatureProps[] = [
{
title: 'Актуальность рынка',
description: 'Данные исследований за 2025 год',
icon: 'i-lucide-pie-chart',
},
{
title: 'Выбор и проверка помещения',
description: 'Как избежать ошибок и не взять «проблемное» место',
icon: 'i-lucide-home',
},
{
title: 'Юридические вопросы',
description: 'Какие документы нужны и как их правильно оформить',
icon: 'i-lucide-file-text',
},
{
title: 'Ремонт и оборудование',
description: 'Как не переплатить подрядчикам и где искать недорогое оборудование',
icon: 'i-lucide-hammer',
},
{
title: 'Подбор и обучение персонала',
description: 'Где найти поваров и как их обучить стандартам',
icon: 'i-lucide-users',
},
{
title: 'Запуск рекламы',
description: 'И привлечение первых гостей',
icon: 'i-lucide-megaphone',
},
{
title: 'Финансовый расчет',
description: 'Разбор инвестиций, ежемесячных расходов и планируемой прибыли',
icon: 'i-lucide-chart-line',
},
]

useHead({
title: 'Вебинар',
})
</script>
27 changes: 27 additions & 0 deletions apps/webinar/i18n/locales/ru-RU.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Русский",
"short-name": "Рус",
"common": {
"color-mode": {
"title": "Тема",
"select": "Выберите тему интерфейса",
"system": "Системная",
"light": "Светлая",
"dark": "Темная",
"switch": {
"system": "Переключиться на системную тему",
"light": "Переключиться на светлый режим",
"dark": "Переключиться на темный режим"
}
},
"to-home": "На главную"
},
"error": {
"common": "Неверное значение",
"not-selected": "Не выбрано",
"length": {
"invalid": "Недопустимое количество символов"
},
"file-size-or-type": "Недопустимый размер или тип файла"
}
Comment on lines +19 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix typos/diacritics; add missing keys (error.title, common.to-home)

Current text uses decomposed “й” (“й”) and is misspelled; also missing required keys referenced in templates.

   "error": {
+    "title": "Ошибка",
     "common": "Неверное значение",
     "not-selected": "Не выбрано",
     "length": {
       "invalid": "Недопустимое количество символов"
     },
-    "file-size-or-type": "Недопустимый размер или тип файла"
+    "file-size-or-type": "Недопустимый размер или тип файла"
   }

Also add a button label:

   "common": {
     "color-mode": {
       "title": "Тема",
       "select": "Выберите тему интерфейса",
       "system": "Системная",
       "light": "Светлая",
       "dark": "Темная",
       "switch": {
         "system": "Переключиться на системную тему",
         "light": "Переключиться на светлый режим",
         "dark": "Переключиться на темный режим"
       }
-    }
+    },
+    "to-home": "На главную"
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"error": {
"common": "Неверное значение",
"not-selected": "Не выбрано",
"length": {
"invalid": "Недопустимое количество символов"
},
"file-size-or-type": "Недопустимый размер или тип файла"
}
"error": {
"title": "Ошибка",
"common": "Неверное значение",
"not-selected": "Не выбрано",
"length": {
"invalid": "Недопустимое количество символов"
},
"file-size-or-type": "Недопустимый размер или тип файла"
},
"common": {
"color-mode": {
"title": "Тема",
"select": "Выберите тему интерфейса",
"system": "Системная",
"light": "Светлая",
"dark": "Темная",
"switch": {
"system": "Переключиться на системную тему",
"light": "Переключиться на светлый режим",
"dark": "Переключиться на тёмный режим"
}
},
"to-home": "На главную"
},
🤖 Prompt for AI Agents
In apps/webinar/i18n/locales/ru-RU.json around lines 18 to 25, fix the
misspelled/decomposed character in "Недопустимый размер или тип файла" by
replacing it with "Недопустимый размер или тип файла", and add the missing
localization keys referenced by templates: "error.title" (a short title for
error dialogs) and "common.to-home" (button label to return home); ensure keys
are placed at the correct JSON nesting and use consistent Russian text and
proper Unicode composition for all characters.

}
Loading