From 71b974903abaa6aede23c3afe2543a3a1fed4b57 Mon Sep 17 00:00:00 2001 From: Daniil Date: Fri, 27 Feb 2026 23:34:17 +0300 Subject: [PATCH] new features --- .claude/commands/convert-icons.md | 36 + .claude/commands/gc.md | 55 + .claude/commands/gen-api-types.md | 51 + .env | 4 +- AGENTS.md | 88 +- CLAUDE.md | 126 ++ app/(protected)/profile/page.tsx | 11 + app/layout.tsx | 9 +- app/template.tsx | 13 + app/under_maintenance/page.tsx | 9 + bun.lock | 20 +- next.config.mjs | 13 + package.json | 6 +- .../NavigationDrawer.module.scss | 97 +- .../NavigationDrawer/NavigationDrawer.tsx | 21 +- src/entities/ProjectCard/ProjectCard.d.ts | 3 + .../ProjectCard/ProjectCard.module.scss | 71 +- src/entities/ProjectCard/ProjectCard.tsx | 20 +- .../UserDropdown/UserDropdown.module.scss | 19 +- src/entities/UserDropdown/UserDropdown.tsx | 79 +- src/entities/UserDropdown/constants.ts | 10 +- src/features/.gitkeep | 0 .../NotificationBell/NotificationBell.d.ts | 3 + .../NotificationBell.module.scss | 29 + .../NotificationBell/NotificationBell.tsx | 45 + .../notifications/NotificationBell/index.ts | 1 + .../NotificationPopup/NotificationPopup.d.ts | 6 + .../NotificationPopup.module.scss | 152 +++ .../NotificationPopup/NotificationPopup.tsx | 177 +++ .../notifications/NotificationPopup/index.ts | 1 + src/features/notifications/index.ts | 2 + .../profile/AvatarUpload/AvatarUpload.d.ts | 5 + .../AvatarUpload/AvatarUpload.module.scss | 38 + .../profile/AvatarUpload/AvatarUpload.tsx | 81 ++ src/features/profile/AvatarUpload/index.ts | 1 + .../ChangePasswordForm.d.ts | 3 + .../ChangePasswordForm.module.scss | 17 + .../ChangePasswordForm/ChangePasswordForm.tsx | 108 ++ .../profile/ChangePasswordForm/index.ts | 1 + .../EditProfileForm/EditProfileForm.d.ts | 6 + .../EditProfileForm.module.scss | 17 + .../EditProfileForm/EditProfileForm.tsx | 102 ++ src/features/profile/EditProfileForm/index.ts | 1 + .../profile/LogoutButton/LogoutButton.d.ts | 3 + .../LogoutButton/LogoutButton.module.scss | 2 + .../profile/LogoutButton/LogoutButton.tsx | 43 + src/features/profile/LogoutButton/index.ts | 1 + src/features/profile/index.ts | 4 + .../ConvertMediaView/ConvertMediaView.d.ts | 6 + .../ConvertMediaView.module.scss | 69 ++ .../ConvertMediaView/ConvertMediaView.tsx | 142 +++ .../project/ConvertMediaView/index.ts | 1 + .../CreateProjectModal.d.ts | 0 .../CreateProjectModal.module.scss | 4 +- .../CreateProjectModal/CreateProjectModal.tsx | 53 +- .../{ => project}/CreateProjectModal/index.ts | 0 .../CreateProjectModal/useCreateProject.ts | 0 .../DeleteFileModal/DeleteFileModal.d.ts | 9 + .../DeleteFileModal.module.scss | 20 + .../DeleteFileModal/DeleteFileModal.tsx | 53 + src/features/project/DeleteFileModal/index.ts | 3 + .../project/DeleteFileModal/useDeleteFile.ts | 48 + .../DeleteProjectModal.d.ts | 11 + .../DeleteProjectModal.module.scss | 20 + .../DeleteProjectModal/DeleteProjectModal.tsx | 66 + .../project/DeleteProjectModal/index.ts | 3 + .../DeleteProjectModal/useDeleteProject.ts | 20 + .../EditProjectModal/EditProjectModal.d.ts | 11 + .../EditProjectModal.module.scss | 25 + .../EditProjectModal/EditProjectModal.tsx | 144 +++ .../project/EditProjectModal/index.ts | 3 + .../EditProjectModal/useUpdateProject.ts | 25 + .../RenameProjectModal.d.ts | 11 + .../RenameProjectModal.module.scss | 15 + .../RenameProjectModal/RenameProjectModal.tsx | 98 ++ .../project/RenameProjectModal/index.ts | 3 + .../SegmentEditModal/SegmentEditModal.d.ts | 19 + .../SegmentEditModal.module.scss | 80 ++ .../SegmentEditModal/SegmentEditModal.tsx | 220 ++++ .../project/SegmentEditModal/index.ts | 1 + .../SegmentSplitter/SegmentSplitter.d.ts | 7 + .../SegmentSplitter.module.scss | 150 +++ .../SegmentSplitter/SegmentSplitter.tsx | 93 ++ src/features/project/SegmentSplitter/index.ts | 1 + .../SilenceResultModal.d.ts | 13 + .../SilenceResultModal.module.scss | 234 ++++ .../SilenceResultModal/SilenceResultModal.tsx | 801 ++++++++++++ .../project/SilenceResultModal/index.ts | 1 + .../useSubmitSilenceApply.ts | 25 + .../SilenceSettingsModal.d.ts | 5 + .../SilenceSettingsModal.module.scss | 49 + .../SilenceSettingsModal.tsx | 184 +++ .../project/SilenceSettingsModal/index.ts | 1 + .../useSubmitSilenceDetect.ts | 25 + .../project/SilenceTrack/SilenceTrack.d.ts | 3 + .../SilenceTrack/SilenceTrack.module.scss | 2 + .../project/SilenceTrack/SilenceTrack.tsx | 14 + src/features/project/SilenceTrack/index.ts | 1 + .../SubtitlesTrack/SubtitlesTrack.d.ts | 10 + .../SubtitlesTrack/SubtitlesTrack.module.scss | 90 ++ .../project/SubtitlesTrack/SubtitlesTrack.tsx | 443 +++++++ src/features/project/SubtitlesTrack/index.ts | 1 + .../TranscriptionEditor.d.ts | 3 + .../TranscriptionEditor.module.scss | 207 ++++ .../TranscriptionEditor.tsx | 263 ++++ .../project/TranscriptionEditor/index.ts | 1 + .../TranscriptionModal.d.ts | 5 + .../TranscriptionModal.module.scss | 25 + .../TranscriptionModal/TranscriptionModal.tsx | 203 ++++ .../project/TranscriptionModal/index.ts | 1 + .../useSubmitTranscription.ts | 24 + .../VideoFramesTrack/VideoFramesTrack.d.ts | 9 + .../VideoFramesTrack.module.scss | 14 + .../VideoFramesTrack/VideoFramesTrack.tsx | 266 ++++ .../project/VideoFramesTrack/index.ts | 1 + .../project/WaveformTrack/WaveformTrack.d.ts | 6 + .../WaveformTrack/WaveformTrack.module.scss | 5 + .../project/WaveformTrack/WaveformTrack.tsx | 169 +++ src/features/project/WaveformTrack/index.ts | 1 + src/features/project/index.ts | 16 + src/pages/HomePage/HomePage.tsx | 8 +- src/pages/LoginPage/LoginPage.module.scss | 32 +- src/pages/ProfilePage/ProfilePage.d.ts | 3 + src/pages/ProfilePage/ProfilePage.module.scss | 47 + src/pages/ProfilePage/ProfilePage.tsx | 6 +- src/pages/ProfilePage/index.ts | 1 + .../ProjectWorkspacePage.module.scss | 1 + .../ProjectWorkspacePage.tsx | 52 +- .../ProjectsPage/ProjectsPage.module.scss | 23 +- src/pages/ProjectsPage/ProjectsPage.tsx | 40 +- .../RegisterPage/RegisterPage.module.scss | 38 +- .../UnderMaintenancePage.d.ts | 3 + .../UnderMaintenancePage.module.scss | 42 + .../UnderMaintenancePage.tsx | 43 + src/pages/UnderMaintenancePage/index.ts | 1 + src/shared/api/__generated__/openapi.types.ts | 1081 ++++++++++++++++- src/shared/api/uploadFile.ts | 89 ++ src/shared/context/AppProviders.tsx | 21 +- src/shared/context/SocketProvider.tsx | 289 +++++ src/shared/context/ThemeSync.tsx | 71 ++ src/shared/context/WorkspaceContext.tsx | 174 +++ src/shared/hooks/useDebounce.ts | 12 + src/shared/hooks/useSegmentResize.ts | 90 ++ src/shared/hooks/useTimelineTracks.ts | 47 + src/shared/lib/dates.ts | 11 + src/shared/lib/transcriptionDocument.ts | 155 +++ src/shared/store/appState/index.ts | 13 +- src/shared/store/appState/types.ts | 6 +- src/shared/store/index.ts | 2 + src/shared/store/notifications/index.ts | 85 ++ src/shared/styles/_variables.scss | 15 +- src/shared/styles/global.scss | 55 +- src/shared/ui/Avatar/Avatar.module.scss | 6 +- src/shared/ui/Avatar/Avatar.tsx | 26 +- src/shared/ui/Button/Button.tsx | 6 +- src/shared/ui/Card/Card.module.scss | 8 +- src/shared/ui/Dropdown/Dropdown.module.scss | 52 +- src/shared/ui/Loader/Loader.module.scss | 11 - src/shared/ui/Modal/Modal.module.scss | 15 +- src/shared/ui/Pagination/Pagination.tsx | 10 +- src/shared/ui/TextField/TextField.module.scss | 27 +- src/widgets/Header/Header.module.scss | 6 + src/widgets/Header/Header.tsx | 2 + .../ProjectsHeader/ProjectsHeader.d.ts | 6 + .../ProjectsHeader/ProjectsHeader.module.scss | 5 +- .../ProjectsHeader/ProjectsHeader.tsx | 40 +- src/widgets/TimelinePanel/TimelinePanel.d.ts | 5 + .../TimelinePanel/TimelinePanel.module.scss | 207 ++++ src/widgets/TimelinePanel/TimelinePanel.tsx | 648 ++++++++++ src/widgets/TimelinePanel/index.ts | 1 + .../Workspace/ActionPanel/ActionPanel.d.ts | 13 + .../ActionPanel/ActionPanel.module.scss | 128 ++ .../Workspace/ActionPanel/ActionPanel.tsx | 321 +++++ src/widgets/Workspace/ActionPanel/index.ts | 1 + src/widgets/Workspace/FileTree/FileTree.d.ts | 4 + .../Workspace/FileTree/FileTree.module.scss | 326 +++++ src/widgets/Workspace/FileTree/FileTree.tsx | 613 ++++++++++ src/widgets/Workspace/FileTree/index.ts | 1 + .../PlayerPlaceholder/PlayerPlaceholder.d.ts | 3 + .../PlayerPlaceholder.module.scss | 33 + .../PlayerPlaceholder/PlayerPlaceholder.tsx | 22 + .../Workspace/PlayerPlaceholder/index.ts | 1 + .../Workspace/VideoPlayer/VideoPlayer.d.ts | 4 + .../VideoPlayer/VideoPlayer.module.scss | 94 ++ .../Workspace/VideoPlayer/VideoPlayer.tsx | 237 ++++ src/widgets/Workspace/VideoPlayer/index.ts | 1 + .../WorkspaceLayout/WorkspaceLayout.d.ts | 7 + .../WorkspaceLayout.module.scss | 38 + .../WorkspaceLayout/WorkspaceLayout.tsx | 18 + .../Workspace/WorkspaceLayout/index.ts | 1 + src/widgets/Workspace/index.ts | 5 + 191 files changed, 11300 insertions(+), 373 deletions(-) create mode 100644 .claude/commands/convert-icons.md create mode 100644 .claude/commands/gc.md create mode 100644 .claude/commands/gen-api-types.md create mode 100644 CLAUDE.md create mode 100644 app/(protected)/profile/page.tsx create mode 100644 app/under_maintenance/page.tsx delete mode 100644 src/features/.gitkeep create mode 100644 src/features/notifications/NotificationBell/NotificationBell.d.ts create mode 100644 src/features/notifications/NotificationBell/NotificationBell.module.scss create mode 100644 src/features/notifications/NotificationBell/NotificationBell.tsx create mode 100644 src/features/notifications/NotificationBell/index.ts create mode 100644 src/features/notifications/NotificationPopup/NotificationPopup.d.ts create mode 100644 src/features/notifications/NotificationPopup/NotificationPopup.module.scss create mode 100644 src/features/notifications/NotificationPopup/NotificationPopup.tsx create mode 100644 src/features/notifications/NotificationPopup/index.ts create mode 100644 src/features/notifications/index.ts create mode 100644 src/features/profile/AvatarUpload/AvatarUpload.d.ts create mode 100644 src/features/profile/AvatarUpload/AvatarUpload.module.scss create mode 100644 src/features/profile/AvatarUpload/AvatarUpload.tsx create mode 100644 src/features/profile/AvatarUpload/index.ts create mode 100644 src/features/profile/ChangePasswordForm/ChangePasswordForm.d.ts create mode 100644 src/features/profile/ChangePasswordForm/ChangePasswordForm.module.scss create mode 100644 src/features/profile/ChangePasswordForm/ChangePasswordForm.tsx create mode 100644 src/features/profile/ChangePasswordForm/index.ts create mode 100644 src/features/profile/EditProfileForm/EditProfileForm.d.ts create mode 100644 src/features/profile/EditProfileForm/EditProfileForm.module.scss create mode 100644 src/features/profile/EditProfileForm/EditProfileForm.tsx create mode 100644 src/features/profile/EditProfileForm/index.ts create mode 100644 src/features/profile/LogoutButton/LogoutButton.d.ts create mode 100644 src/features/profile/LogoutButton/LogoutButton.module.scss create mode 100644 src/features/profile/LogoutButton/LogoutButton.tsx create mode 100644 src/features/profile/LogoutButton/index.ts create mode 100644 src/features/profile/index.ts create mode 100644 src/features/project/ConvertMediaView/ConvertMediaView.d.ts create mode 100644 src/features/project/ConvertMediaView/ConvertMediaView.module.scss create mode 100644 src/features/project/ConvertMediaView/ConvertMediaView.tsx create mode 100644 src/features/project/ConvertMediaView/index.ts rename src/features/{ => project}/CreateProjectModal/CreateProjectModal.d.ts (100%) rename src/features/{ => project}/CreateProjectModal/CreateProjectModal.module.scss (75%) rename src/features/{ => project}/CreateProjectModal/CreateProjectModal.tsx (70%) rename src/features/{ => project}/CreateProjectModal/index.ts (100%) rename src/features/{ => project}/CreateProjectModal/useCreateProject.ts (100%) create mode 100644 src/features/project/DeleteFileModal/DeleteFileModal.d.ts create mode 100644 src/features/project/DeleteFileModal/DeleteFileModal.module.scss create mode 100644 src/features/project/DeleteFileModal/DeleteFileModal.tsx create mode 100644 src/features/project/DeleteFileModal/index.ts create mode 100644 src/features/project/DeleteFileModal/useDeleteFile.ts create mode 100644 src/features/project/DeleteProjectModal/DeleteProjectModal.d.ts create mode 100644 src/features/project/DeleteProjectModal/DeleteProjectModal.module.scss create mode 100644 src/features/project/DeleteProjectModal/DeleteProjectModal.tsx create mode 100644 src/features/project/DeleteProjectModal/index.ts create mode 100644 src/features/project/DeleteProjectModal/useDeleteProject.ts create mode 100644 src/features/project/EditProjectModal/EditProjectModal.d.ts create mode 100644 src/features/project/EditProjectModal/EditProjectModal.module.scss create mode 100644 src/features/project/EditProjectModal/EditProjectModal.tsx create mode 100644 src/features/project/EditProjectModal/index.ts create mode 100644 src/features/project/EditProjectModal/useUpdateProject.ts create mode 100644 src/features/project/RenameProjectModal/RenameProjectModal.d.ts create mode 100644 src/features/project/RenameProjectModal/RenameProjectModal.module.scss create mode 100644 src/features/project/RenameProjectModal/RenameProjectModal.tsx create mode 100644 src/features/project/RenameProjectModal/index.ts create mode 100644 src/features/project/SegmentEditModal/SegmentEditModal.d.ts create mode 100644 src/features/project/SegmentEditModal/SegmentEditModal.module.scss create mode 100644 src/features/project/SegmentEditModal/SegmentEditModal.tsx create mode 100644 src/features/project/SegmentEditModal/index.ts create mode 100644 src/features/project/SegmentSplitter/SegmentSplitter.d.ts create mode 100644 src/features/project/SegmentSplitter/SegmentSplitter.module.scss create mode 100644 src/features/project/SegmentSplitter/SegmentSplitter.tsx create mode 100644 src/features/project/SegmentSplitter/index.ts create mode 100644 src/features/project/SilenceResultModal/SilenceResultModal.d.ts create mode 100644 src/features/project/SilenceResultModal/SilenceResultModal.module.scss create mode 100644 src/features/project/SilenceResultModal/SilenceResultModal.tsx create mode 100644 src/features/project/SilenceResultModal/index.ts create mode 100644 src/features/project/SilenceResultModal/useSubmitSilenceApply.ts create mode 100644 src/features/project/SilenceSettingsModal/SilenceSettingsModal.d.ts create mode 100644 src/features/project/SilenceSettingsModal/SilenceSettingsModal.module.scss create mode 100644 src/features/project/SilenceSettingsModal/SilenceSettingsModal.tsx create mode 100644 src/features/project/SilenceSettingsModal/index.ts create mode 100644 src/features/project/SilenceSettingsModal/useSubmitSilenceDetect.ts create mode 100644 src/features/project/SilenceTrack/SilenceTrack.d.ts create mode 100644 src/features/project/SilenceTrack/SilenceTrack.module.scss create mode 100644 src/features/project/SilenceTrack/SilenceTrack.tsx create mode 100644 src/features/project/SilenceTrack/index.ts create mode 100644 src/features/project/SubtitlesTrack/SubtitlesTrack.d.ts create mode 100644 src/features/project/SubtitlesTrack/SubtitlesTrack.module.scss create mode 100644 src/features/project/SubtitlesTrack/SubtitlesTrack.tsx create mode 100644 src/features/project/SubtitlesTrack/index.ts create mode 100644 src/features/project/TranscriptionEditor/TranscriptionEditor.d.ts create mode 100644 src/features/project/TranscriptionEditor/TranscriptionEditor.module.scss create mode 100644 src/features/project/TranscriptionEditor/TranscriptionEditor.tsx create mode 100644 src/features/project/TranscriptionEditor/index.ts create mode 100644 src/features/project/TranscriptionModal/TranscriptionModal.d.ts create mode 100644 src/features/project/TranscriptionModal/TranscriptionModal.module.scss create mode 100644 src/features/project/TranscriptionModal/TranscriptionModal.tsx create mode 100644 src/features/project/TranscriptionModal/index.ts create mode 100644 src/features/project/TranscriptionModal/useSubmitTranscription.ts create mode 100644 src/features/project/VideoFramesTrack/VideoFramesTrack.d.ts create mode 100644 src/features/project/VideoFramesTrack/VideoFramesTrack.module.scss create mode 100644 src/features/project/VideoFramesTrack/VideoFramesTrack.tsx create mode 100644 src/features/project/VideoFramesTrack/index.ts create mode 100644 src/features/project/WaveformTrack/WaveformTrack.d.ts create mode 100644 src/features/project/WaveformTrack/WaveformTrack.module.scss create mode 100644 src/features/project/WaveformTrack/WaveformTrack.tsx create mode 100644 src/features/project/WaveformTrack/index.ts create mode 100644 src/features/project/index.ts create mode 100644 src/pages/ProfilePage/ProfilePage.d.ts create mode 100644 src/pages/ProfilePage/ProfilePage.module.scss create mode 100644 src/pages/ProfilePage/index.ts create mode 100644 src/pages/UnderMaintenancePage/UnderMaintenancePage.d.ts create mode 100644 src/pages/UnderMaintenancePage/UnderMaintenancePage.module.scss create mode 100644 src/pages/UnderMaintenancePage/UnderMaintenancePage.tsx create mode 100644 src/pages/UnderMaintenancePage/index.ts create mode 100644 src/shared/api/uploadFile.ts create mode 100644 src/shared/context/SocketProvider.tsx create mode 100644 src/shared/context/ThemeSync.tsx create mode 100644 src/shared/context/WorkspaceContext.tsx create mode 100644 src/shared/hooks/useDebounce.ts create mode 100644 src/shared/hooks/useSegmentResize.ts create mode 100644 src/shared/hooks/useTimelineTracks.ts create mode 100644 src/shared/lib/dates.ts create mode 100644 src/shared/lib/transcriptionDocument.ts create mode 100644 src/shared/store/notifications/index.ts create mode 100644 src/widgets/TimelinePanel/TimelinePanel.d.ts create mode 100644 src/widgets/TimelinePanel/TimelinePanel.module.scss create mode 100644 src/widgets/TimelinePanel/TimelinePanel.tsx create mode 100644 src/widgets/TimelinePanel/index.ts create mode 100644 src/widgets/Workspace/ActionPanel/ActionPanel.d.ts create mode 100644 src/widgets/Workspace/ActionPanel/ActionPanel.module.scss create mode 100644 src/widgets/Workspace/ActionPanel/ActionPanel.tsx create mode 100644 src/widgets/Workspace/ActionPanel/index.ts create mode 100644 src/widgets/Workspace/FileTree/FileTree.d.ts create mode 100644 src/widgets/Workspace/FileTree/FileTree.module.scss create mode 100644 src/widgets/Workspace/FileTree/FileTree.tsx create mode 100644 src/widgets/Workspace/FileTree/index.ts create mode 100644 src/widgets/Workspace/PlayerPlaceholder/PlayerPlaceholder.d.ts create mode 100644 src/widgets/Workspace/PlayerPlaceholder/PlayerPlaceholder.module.scss create mode 100644 src/widgets/Workspace/PlayerPlaceholder/PlayerPlaceholder.tsx create mode 100644 src/widgets/Workspace/PlayerPlaceholder/index.ts create mode 100644 src/widgets/Workspace/VideoPlayer/VideoPlayer.d.ts create mode 100644 src/widgets/Workspace/VideoPlayer/VideoPlayer.module.scss create mode 100644 src/widgets/Workspace/VideoPlayer/VideoPlayer.tsx create mode 100644 src/widgets/Workspace/VideoPlayer/index.ts create mode 100644 src/widgets/Workspace/WorkspaceLayout/WorkspaceLayout.d.ts create mode 100644 src/widgets/Workspace/WorkspaceLayout/WorkspaceLayout.module.scss create mode 100644 src/widgets/Workspace/WorkspaceLayout/WorkspaceLayout.tsx create mode 100644 src/widgets/Workspace/WorkspaceLayout/index.ts create mode 100644 src/widgets/Workspace/index.ts diff --git a/.claude/commands/convert-icons.md b/.claude/commands/convert-icons.md new file mode 100644 index 0000000..5e34bfc --- /dev/null +++ b/.claude/commands/convert-icons.md @@ -0,0 +1,36 @@ +# Convert Icons + +Convert raw SVG icons to React TSX components using SVGR. + +## Usage + +``` +/convert-icons +``` + +## Instructions + +1. Check that raw SVG files exist in `src/shared/assets/raw-icons/`. If the directory doesn't exist or is empty, inform the user and ask them to place SVG files there first. + +2. Run the conversion: + ```bash + bun run gicons + ``` + This executes: + ``` + npx @svgr/cli --ext tsx --typescript --no-prettier --icon --ref --no-svgo ./src/shared/assets/raw-icons/ --out-dir ./src/shared/ui/Icons/ + ``` + +3. Report which icon components were generated in `src/shared/ui/Icons/`. + +4. The generated components can be imported as: + ```tsx + import { IconName } from "@shared/ui/Icons/IconName" + ``` + +## Notes + +- Raw SVGs go in: `src/shared/assets/raw-icons/` +- Generated TSX components output to: `src/shared/ui/Icons/` +- SVGR flags: TypeScript, icon mode (scales with font-size), forwardRef support, no Prettier formatting, no SVGO optimization +- The primary icon library is `lucide-react` — custom SVG icons are for icons not available in Lucide diff --git a/.claude/commands/gc.md b/.claude/commands/gc.md new file mode 100644 index 0000000..af33557 --- /dev/null +++ b/.claude/commands/gc.md @@ -0,0 +1,55 @@ +# Generate Component (gc) + +Generate an FSD component using the project's generator script. + +## Usage + +``` +/gc +``` + +**Arguments:** +- `$ARGUMENTS` — expects ` `, e.g. `shared Button`, `entity ProjectCard`, `feature CreateProjectModal` + +## Layers + +| Alias | Path | +|-------|------| +| `shared` | `src/shared/ui/` | +| `entity` / `entities` | `src/entities/` | +| `feature` / `features` | `src/features/` | +| `widget` / `widgets` | `src/widgets/` | +| `page` / `pages` | `src/pages/` | + +## Instructions + +1. Parse `$ARGUMENTS` to extract `` and ``. If arguments are missing or unclear, ask the user. + +2. Run the generator: + ```bash + bun run gc + ``` + +3. This creates 4 files: + - `index.ts` — re-exports the component + - `.tsx` — component implementation with `FunctionComponent`, `JSX.Element`, SCSS module import, `data-testid` + - `.d.ts` — props interface `IProps` with `className?: string` + - `.module.scss` — empty `.root {}` class + +4. After generation, report the created files and the full path. + +5. If the user provides additional context about what the component should do or look like, modify the generated files accordingly: + - Add props to the `.d.ts` file + - Implement the component logic in `.tsx` + - Add styles to `.module.scss` + - Use existing project patterns: `classnames` as `cs`, typography/mixin includes from auto-injected SCSS partials, Radix UI primitives or Themes where appropriate, `lucide-react` icons + +## Project Conventions + +- Props interface: `IProps` in a `.d.ts` file +- Import types with `import type { ... }` +- Import order: types → react/libs → path aliases (`@shared/`, `@entities/`, etc.) → local +- Use `cs()` from `classnames` for combining classes +- Root element gets `className={styles.root}` and `data-testid=""` +- SCSS auto-injected namespaces: `variables`, `breakpoints`, `typography`, `mixins` +- Use `"use client"` directive only when component uses hooks or browser APIs diff --git a/.claude/commands/gen-api-types.md b/.claude/commands/gen-api-types.md new file mode 100644 index 0000000..2e93a9e --- /dev/null +++ b/.claude/commands/gen-api-types.md @@ -0,0 +1,51 @@ +# Generate API Types + +Fetch the OpenAPI schema from the backend and generate TypeScript types. + +## Usage + +``` +/gen-api-types +``` + +## Instructions + +1. Ensure the backend server is running at `http://127.0.0.1:8000`. If the command fails with a connection error, inform the user that the backend must be running first. + +2. Run the type generation: + ```bash + bun run gen:api-types + ``` + This executes: + ``` + openapi-typescript http://127.0.0.1:8000/api/schema/ --output src/shared/api/__generated__/openapi.types.ts + ``` + +3. Report success and note that the generated types are at `src/shared/api/__generated__/openapi.types.ts`. + +4. The generated file exports: + - `paths` — all API endpoints with request/response types + - `components` — schema definitions (used as `components["schemas"]["ModelName"]`) + - `operations` — operation-level types + +## Usage in Code + +```tsx +// Import schema types +import type { components } from "@shared/api/__generated__/openapi.types" + +type ProjectRead = components["schemas"]["ProjectRead"] + +// Use with the API client +import api from "@shared/api" + +api.useQuery("get", "/api/projects/") +api.useMutation("post", "/api/projects/", { onSuccess, onError }) +``` + +## Notes + +- Requires the backend running at `http://127.0.0.1:8000` +- Uses `openapi-typescript` to generate types from the `/api/schema/` endpoint +- The API client (`src/shared/api/index.ts`) uses `openapi-fetch` + `openapi-react-query` with these generated types +- After regeneration, check for any TypeScript errors in components that use the API types, as schema changes may break existing code diff --git a/.env b/.env index fefb75e..7c4ece2 100644 --- a/.env +++ b/.env @@ -1 +1,3 @@ -NEXT_PUBLIC_API_URL=http://localhost:8000/ \ No newline at end of file +NEXT_PUBLIC_API_URL=http://localhost:8000 +NEXT_PUBLIC_WS_URL=ws://localhost:8000 +NEXT_PUBLIC_MOCK_WS=false \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index abf49c0..8a846aa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Next.js 16 application using **Feature-Sliced Design (FSD)** architecture, power | Styling | SCSS Modules, normalize.css | | State/Fetch | TanStack React Query 5, Axios, Xior | | Animation | Framer Motion | -| Utilities | Lodash, Moment.js, classnames, usehooks-ts | +| Utilities | Lodash, date-fns, classnames, usehooks-ts | | Icons | Lucide React, SVGR (custom icons) | | Notifications | React Toastify | | File Upload | React Dropzone | @@ -174,10 +174,12 @@ export const Button: FC = ({ variant, onClick }): JSX.Element => { 2. **Public API** — export only through `index.ts` 3. **No Cross-Slice Imports** — features cannot import from other features 4. **Shared is Agnostic** — no business logic in shared layer +5. **Features are module-aware** — group features by domain inside module folders (see below) ### When to Split Files Split into separate files **only when**: + - Hook/API is reused by multiple components - File exceeds ~200 lines - Props interface is shared across 3+ components @@ -208,6 +210,72 @@ Split into separate files **only when**: --- +## Features Layer — Module-Aware Structure + +Features **must be grouped by domain module**. Never place feature folders flat at the top of `src/features/`. + +``` +src/features/ +├── profile/ # Profile domain module +│ ├── index.ts # Barrel export for all features in this module +│ ├── AvatarUpload/ +│ ├── EditProfileForm/ +│ └── LogoutButton/ +└── project/ # Project domain module + ├── index.ts + ├── CreateProjectModal/ + └── ... +``` + +**Rules:** +- Each module folder has an `index.ts` barrel that re-exports all its features +- Import via the module barrel: `import { AvatarUpload } from "@features/profile"` +- When creating a new feature, place it inside the relevant domain folder +- After running `bun run gc feature `, move the generated folder into the correct module +- Create a new module folder + barrel if the domain doesn't exist yet + +--- + +## Shared Utilities + +Reusable operations should live in `src/shared/` — **do not inline shared logic inside feature components**. + +### File Upload + +Use `uploadFile()` from `@shared/api/uploadFile` for any file upload: + +```ts +import { uploadFile } from "@shared/api/uploadFile" + +const result = await uploadFile(file, "avatars") +// result.file_url — URL of the uploaded file +// result.file_path — storage path +``` + +This handles FormData construction, Content-Type header override, and JWT auth automatically. + +### Date Formatting + +Use `date-fns` with Russian locale via shared utilities in `src/shared/lib/dates.ts`. **Never use `moment.js` or inline `Date` formatting in components.** + +```ts +import { formatDate, formatRelativeTime } from "@shared/lib/dates" + +formatDate(user.date_joined) // "21.02.2026" (default: "dd.MM.yyyy") +formatDate(date, "dd MMM yyyy") // "21 февр. 2026" +formatRelativeTime(project.updated_at) // "2 дня назад" +``` + +Add new date helpers to `src/shared/lib/dates.ts`, not to individual components. + +### API Client + +- **In React components**: use `api.useQuery()` / `api.useMutation()` from `@shared/api` +- **Outside React** (utilities, event handlers): use `fetchClient` from `@shared/api` +- **File uploads**: use `uploadFile()` from `@shared/api/uploadFile` + +--- + ## Icons Workflow 1. Place raw SVG in `src/shared/assets/raw-icons/` @@ -236,6 +304,10 @@ Split into separate files **only when**: | API client | Use Axios/Xior with React Query | | State management | TanStack Query (server state) | +## Localization + +All user-facing UI text **must be in Russian**. This includes: labels, headings, buttons, placeholders, tooltips, aria-labels, error messages, breadcrumbs, and any other text visible to the user. The only exception is the brand name "Coffee Project" / "Cofee Project" — it stays in English. + ## Implementation sentiments Write less complicated code, simple but readable code @@ -245,3 +317,17 @@ To import classNames lib use `import cs from 'classnames'` Always install packages using `bun install ` +To test is project have no errors use +`bunx tsc --noEmit` + +--- + +## Common Mistakes to Avoid + +1. **Flat features folder** — never place feature component folders directly in `src/features/`. Always group them inside a domain module folder (`profile/`, `project/`, etc.). +2. **Inlining reusable logic** — if an operation (file upload, date formatting, etc.) could be used by multiple features, extract it to `src/shared/`. Features should be thin wrappers around shared utilities. +3. **Wrong StaticLoader import** — it lives at `@shared/ui/Loader`, not `@shared/ui/Loader/StaticLoader`. There is no subdirectory. +4. **multipart/form-data with fetchClient** — the default `fetchClient` sets `Content-Type: application/json`. For file uploads you must override headers and body serializer. Use the shared `uploadFile()` utility instead. +5. **Broken lint scripts** — `bun run lint` calls `lint:es` and `lint:prettier` which are not defined in `package.json`. Use `bunx tsc --noEmit` for type checking until lint is fixed. +6. **Generator output needs moving** — `bun run gc feature ` creates the folder flat in `src/features/`. You must manually move it into the correct domain module folder afterward. +7. **Raw `fetch` / `useEffect` for API calls** — never use plain `fetch` or `useEffect`-based polling for API requests. Always use `api.useQuery()` / `api.useMutation()` from `@shared/api` which wraps TanStack Query + openapi-fetch. For polling, use `refetchInterval`. Raw `fetch` bypasses typed routes, auth middleware, and query caching. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..54c65fe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,126 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +See also the monorepo-level `../CLAUDE.md` for full architecture overview and backend docs. + +## Commands + +```bash +bun dev # Dev server (localhost:3000) +bun run build # Production build +bun run lint # ESLint + Prettier (concurrent) +bunx tsc --noEmit # Type-check without emitting +bun run gc # Generate FSD component (e.g. bun run gc shared Button) +bun run gicons # Convert raw SVGs → React icon components +bun run gen:api-types # Regenerate API types from OpenAPI schema (backend must be running) +``` + +## Architecture + +Next.js 16 App Router with Feature-Sliced Design. Strict unidirectional imports: `pages → widgets → features → entities → shared`. + +- **App directory** is at `app/` (project root), not `src/app/`. The `src/app/` layer holds global styles and providers. +- **`app/template.tsx`** wraps all routes with Header (conditionally hidden on auth routes). +- **All components are `"use client"`** unless explicitly marked otherwise. + +## API & Data Layer + +- **`fetchClient`** (`openapi-fetch`) — typed HTTP client with JWT middleware that reads `access_token` from cookies. Defined in `src/shared/api/index.ts`. +- **`api`** (`openapi-react-query`) — wraps `fetchClient` for use with TanStack Query hooks in components. Import as `import api from "@shared/api"`. +- **Generated types** live in `src/shared/api/__generated__/openapi.types.ts` — never edit manually. +- **Server actions** in `src/shared/api/server.ts` — used for server-side API calls (ping, token verification). + +## Styling + +- **SCSS Modules** (`.module.scss`) for all component styles. +- **SCSS partials auto-injected** via `next.config.mjs` using `@use`: `_variables.scss`, `_breakpoints.scss`, `_typography.scss`, `_mixins.scss`. No need to import them manually in `.module.scss` files. +- **Radix UI Themes** wraps the app (`accentColor="iris"`, `grayColor="slate"`). Some components use Radix primitives directly (e.g., Dropdown uses `@radix-ui/react-dropdown-menu`, not Radix Themes). +- **Class composition**: `import cs from "classnames"`. +- **Design tokens** defined as CSS custom properties in `src/shared/styles/global.scss`, mirrored as SCSS vars in `_variables.scss`. + +## State Management + +- **Server state**: TanStack React Query (primary for all API data). +- **Client state**: Redux Toolkit with two slices: `appState` and `user` (in `src/shared/store/`). +- **Provider hierarchy** (in `src/shared/context/AppProviders.tsx`): Redux → QueryClient → UserSync → Radix Theme. + +## Component Convention + +Generate new components with `bun run gc ` — never create component files manually. Each component folder contains: +- `index.ts` — public re-export only +- `ComponentName.tsx` — implementation +- `ComponentName.module.scss` — scoped styles +- `ComponentName.d.ts` — props interface (`IComponentNameProps`) + +## Code Style + +- **Prettier**: tabs (width 2), no semicolons, double quotes, sorted imports. +- **Imports**: use path aliases (`@shared/*`, `@entities/*`, etc.), never relative paths across layers. +- **Forms**: `react-hook-form` for form state management. +- **Icons**: Lucide React for standard icons. Custom icons: place SVG in `src/shared/assets/raw-icons/`, run `bun run gicons`, import from `@shared/ui/Icons/IconName`. + +## Features Layer — Module-Aware Structure + +Features are **grouped by domain module**, not placed flat at the top level. Each module folder has a barrel `index.ts`: + +``` +src/features/ +├── profile/ # Profile domain +│ ├── index.ts # Barrel: re-exports all features in module +│ ├── AvatarUpload/ +│ ├── EditProfileForm/ +│ └── LogoutButton/ +└── project/ # Project domain + ├── index.ts + ├── CreateProjectModal/ + ├── DeleteProjectModal/ + ├── EditProjectModal/ + └── RenameProjectModal/ +``` + +Import via module barrel: `import { AvatarUpload, EditProfileForm } from "@features/profile"`. + +When adding a new feature, place it inside the relevant domain module folder (create one if needed). + +## File Uploads + +Use the shared `uploadFile` utility for any file upload — do not inline FormData logic in components: + +```ts +import { uploadFile } from "@shared/api/uploadFile" +const result = await uploadFile(file, "avatars") +// result.file_url, result.file_path +``` + +The utility handles FormData construction, Content-Type override, and auth middleware automatically. + +## Date Formatting + +Use `date-fns` with Russian locale for all date formatting — never use `moment.js` or inline `Date` logic: + +```ts +import { formatDate, formatRelativeTime } from "@shared/lib/dates" + +formatDate(user.date_joined) // "21.02.2026" +formatDate(date, "dd MMM yyyy") // "21 февр. 2026" +formatRelativeTime(project.updated_at) // "2 дня назад" +``` + +Utilities live in `src/shared/lib/dates.ts`. Add new date helpers there, not in components. + +## Localization + +All user-facing UI text **must be in Russian** — labels, headings, buttons, placeholders, tooltips, aria-labels, error messages, breadcrumbs. The brand name "Coffee Project" / "Cofee Project" stays in English. + +## Gotchas + +- The `pages/` directory in the project root must exist (even if empty) — removing it causes Next.js build errors. +- Dropdown component's `asChild` trigger applies both the Dropdown's trigger class AND the child's class. Avoid `all: unset` on Dropdown triggers — it strips child flex/display styles. +- SCSS auto-import uses `@use` (not `@import`), so variables/mixins are namespaced (e.g., `variables.$color`). The files are injected as `additionalData` in `next.config.mjs`. +- **openapi-fetch + multipart**: `fetchClient` defaults to `Content-Type: application/json`. For file uploads, you must override the header and body serializer. Use the shared `uploadFile()` utility instead of doing this manually. +- **StaticLoader** is exported from `@shared/ui/Loader` (file is `Loader.tsx`), not from `@shared/ui/Loader/StaticLoader` — there is no subdirectory. +- **`lint:es` / `lint:prettier` scripts** are referenced by `bun run lint` but not defined in `package.json`. Linting is currently broken — use `bunx tsc --noEmit` for type checking. +- **`next/image` remote hosts**: External image hostnames must be listed in `next.config.mjs` `images.remotePatterns`. MinIO (`localhost:9000`) is already configured. If you add another storage backend, add its hostname there too. +- **Stale OpenAPI types**: Always run `bun run gen:api-types` before implementing against the API if the backend has changed. Stale types cause silent 404s at runtime. +- **Never use raw `fetch`/`useEffect` for API calls** — always use `api.useQuery()`/`api.useMutation()` from `@shared/api` (TanStack Query + openapi-fetch wrapper). For polling, use the `refetchInterval` option. Raw `fetch` bypasses typed routes, auth middleware, and query caching. diff --git a/app/(protected)/profile/page.tsx b/app/(protected)/profile/page.tsx new file mode 100644 index 0000000..3ccae91 --- /dev/null +++ b/app/(protected)/profile/page.tsx @@ -0,0 +1,11 @@ +import { JSX } from "react" + +import { ProfilePage } from "@pages/ProfilePage" + +export default function Profile(): JSX.Element { + return ( +
+ +
+ ) +} diff --git a/app/layout.tsx b/app/layout.tsx index 9bfba6a..594d114 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -24,7 +24,14 @@ export default function RootLayout({ children: ReactNode }>) { return ( - + + +