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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/v1/
NEXT_PUBLIC_APP_URL=http://localhost:3000
PORT=3000
# Next App FRONTEND Instrumentation
NEXT_PUBLIC_FARO_URL=http://localhost:12347/collect
Expand Down
1 change: 1 addition & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000/v1
NEXT_PUBLIC_APP_URL=http://localhost:3000
PORT=3000
# Next App FRONTEND Instrumentation
NEXT_PUBLIC_FARO_URL=http://localhost:12347/collect
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
file: ./Dockerfile.prod
build-args: |
NEXT_PUBLIC_API_BASE_URL=${{ vars.NEXT_PUBLIC_API_BASE_URL }}
NEXT_PUBLIC_APP_URL=${{ vars.NEXT_PUBLIC_APP_URL }}
NEXT_PUBLIC_FARO_URL=${{ vars.NEXT_PUBLIC_FARO_URL }}
NEXT_PUBLIC_FARO_APP_VERSION=${{ vars.NEXT_PUBLIC_FARO_APP_VERSION }}
NEXT_PUBLIC_APP_ENV=${{ vars.NEXT_PUBLIC_APP_ENV }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
SKIP_ENV_VALIDATION: true
NODE_ENV: production
NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}
NEXT_PUBLIC_APP_URL: ${{vars.NEXT_PUBLIC_APP_URL}}
NEXT_PUBLIC_FARO_URL: ${{ vars.NEXT_PUBLIC_FARO_URL }}
NEXT_PUBLIC_FARO_APP_VERSION: ${{ vars.NEXT_PUBLIC_FARO_APP_VERSION }}
NEXT_PUBLIC_APP_ENV: ${{ vars.NEXT_PUBLIC_APP_ENV }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jobs:
file: ./Dockerfile.prod
build-args: |
NEXT_PUBLIC_API_BASE_URL=${{ vars.NEXT_PUBLIC_API_BASE_URL }}
NEXT_PUBLIC_APP_URL=${{ vars.NEXT_PUBLIC_APP_URL }}
NEXT_PUBLIC_FARO_URL=${{ vars.NEXT_PUBLIC_FARO_URL }}
NEXT_PUBLIC_FARO_APP_VERSION=${{ vars.NEXT_PUBLIC_FARO_APP_VERSION }}
NEXT_PUBLIC_APP_ENV=${{ vars.NEXT_PUBLIC_APP_ENV }}
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile.prod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --offline --froze

FROM base AS builder

ARG NEXT_PUBLIC_APP_URL
ARG NEXT_PUBLIC_API_BASE_URL
ARG NEXT_PUBLIC_FARO_URL
ARG NEXT_PUBLIC_FARO_APP_VERSION
Expand All @@ -24,6 +25,7 @@ ARG NEXT_PUBLIC_METRICS_ENABLED
ARG SKIP_ENV_VALIDATION=false

ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL \
NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL \
NEXT_PUBLIC_FARO_URL=$NEXT_PUBLIC_FARO_URL \
NEXT_PUBLIC_FARO_APP_NAME="frontend" \
NEXT_PUBLIC_FARO_APP_NAMESPACE="frontend" \
Expand Down
4 changes: 2 additions & 2 deletions app/(auth)/oauth/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export async function GET(request: NextRequest) {
);
}

const successUrl = new URL(routes.user.profile(), request.url);
const successUrl = new URL(routes.user.profile(), env.NEXT_PUBLIC_APP_URL);

successUrl.searchParams.set('success', 'true');
successUrl.searchParams.set('message', 'Операция выполнена успешно');
Expand All @@ -76,7 +76,7 @@ export async function GET(request: NextRequest) {
}
}

const errorUrl = new URL(routes.auth.signin(), request.url);
const errorUrl = new URL(routes.auth.signin(), env.NEXT_PUBLIC_APP_URL);

errorUrl.searchParams.set('success', 'false');
errorUrl.searchParams.set('message', ERROR_MESSAGE);
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const eslintConfig = defineConfig([
'src/**/': 'KEBAB_CASE',
},
],
'object-shorthand': ['warn', 'always'],
},
},
globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts']),
Expand Down
36 changes: 18 additions & 18 deletions src/entities/auth/api/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class AuthHttp {
return api<TAuth.SigninResponse>({
url: '/auth/sign-in',
method: 'POST',
data: data,
data,
skipAuthRefresh: true,
contracts: {
body: SAuth.SigninBody,
Expand All @@ -30,7 +30,7 @@ export class AuthHttp {
return api<TAuth.SignupResponse>({
url: '/auth/sign-up',
method: 'POST',
data: data,
data,
contracts: {
body: SAuth.SignupBody,
response: SAuth.SignupResponse,
Expand All @@ -42,7 +42,7 @@ export class AuthHttp {
return api<TAuth.SignupConfirmResponse>({
url: '/auth/sign-up/confirm',
method: 'POST',
data: data,
data,
skipAuthRefresh: true,
contracts: {
body: SAuth.SignupConfirmBody,
Expand All @@ -55,7 +55,7 @@ export class AuthHttp {
return api<TAuth.ResetPasswordResponse>({
url: '/auth/password/reset',
method: 'POST',
data: data,
data,
contracts: {
body: SAuth.ResetPasswordBody,
response: SAuth.ResetPasswordResponse,
Expand All @@ -67,7 +67,7 @@ export class AuthHttp {
return api<TAuth.ResetPasswordVerifyResponse>({
url: '/auth/password/reset/verify',
method: 'POST',
data: data,
data,
contracts: {
body: SAuth.ResetPasswordVerifyBody,
response: SAuth.ResetPasswordVerifyResponse,
Expand All @@ -79,13 +79,24 @@ export class AuthHttp {
return api<TAuth.ResetPasswordConfirmResponse>({
url: '/auth/password/reset/confirm',
method: 'POST',
data: data,
data,
contracts: {
body: SAuth.ResetPasswordConfirmBody,
response: SAuth.ResetPasswordConfirmResponse,
},
});
}
static resendCode(data: TAuth.ResendCodeBody): Promise<TAuth.ResendCodeResponse> {
return api<TAuth.ResendCodeResponse>({
url: '/auth/resend',
method: 'POST',
data,
contracts: {
body: SAuth.ResendCodeBody,
response: SAuth.ResendCodeResponse,
},
});
}
static oAuthProviders(signal: AbortSignal) {
return api<TAuth.OAuthProvidersResponse>({
url: '/oauth/providers',
Expand Down Expand Up @@ -124,22 +135,11 @@ export class AuthHttp {
},
});
}
static resendCode(data: TAuth.ResendCodeBody): Promise<TAuth.ResendCodeResponse> {
return api<TAuth.ResendCodeResponse>({
url: '/oauth/resend',
method: 'POST',
data: data,
contracts: {
body: SAuth.ResendCodeBody,
response: SAuth.ResendCodeResponse,
},
});
}
static exchangeToken(data: TAuth.ExchangeTokenBody): Promise<TAuth.ExchangeTokenResponse> {
return api<TAuth.ExchangeTokenResponse>({
url: '/oauth/exchange',
method: 'POST',
data: data,
data,
});
}
}
2 changes: 1 addition & 1 deletion src/entities/board/model/mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class BoardMapper {
columnList: BoardColumnResponse[],
taskList: unknown[]
): KanbanBoardData {
const sortedColumns = [...columnList].sort((a, b) => a.orderIndex - b.orderIndex);
const sortedColumns = [...columnList].sort((a, b) => a.position - b.position);
const tasksByColumn: Record<string, unknown[]> = {};
const columns: Record<string, BoardColumnResponse> = {};

Expand Down
21 changes: 5 additions & 16 deletions src/entities/board/model/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSortingSchema, DateTimeString, GlobalSuccess } from 'shared/api';
import { createSortingSchema, CursorQuerySchema, DateTimeString, GlobalSuccess } from 'shared/api';
import { z } from 'zod/v4';

export const ActionResponse = GlobalSuccess;
Expand Down Expand Up @@ -84,7 +84,7 @@ export const BoardColumn = z.object({
.nullable()
.optional(),
icon: z.string().max(20, 'Иконка должна быть не длиннее 20 символов').nullable().optional(),
orderIndex: z
position: z
.number()
.int('Порядковый номер должен быть целым числом')
.min(0, 'Порядковый номер не может быть отрицательным'),
Expand Down Expand Up @@ -154,7 +154,7 @@ export const CreateBoardColumnBody = BoardColumn.omit({
autoTransitionTo: true,
stateType: true,
category: true,
orderIndex: true,
position: true,
isVisible: true,
notifyOnEnter: true,
notifyOnExit: true,
Expand All @@ -176,20 +176,9 @@ export const BoardColumnQueryParams = z
my: z.boolean().optional(),
category: z.string().optional(),
overdue: z.boolean().optional(),
page: z.coerce.number().int().positive().optional(),
offset: z.coerce.number().int().min(0).optional(),
limit: z.coerce.number().int().min(0).max(100).optional(),
})
.extend(createSortingSchema(['order', 'title', 'tasksCount', 'createdAt']).shape)
.transform((data) => {
if (data.page && data.page > 1 && data.offset === 0) {
return {
...data,
offset: (data.page - 1) * (data.limit || 20),
};
}
return data;
});
.extend(CursorQuerySchema.shape)
.extend(createSortingSchema(['order', 'title', 'tasksCount', 'createdAt']).shape);

export const UpdateBoardColumnResponse = GlobalSuccess;
export const CreateBoardColumnResponse = GlobalSuccess.extend({
Expand Down
1 change: 1 addition & 0 deletions src/entities/board/model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as SBoard from './schemas';

export type BoardColumnStatus = z.infer<typeof SBoard.ColumnStatusEnum>;
export type BoardViewType = z.infer<typeof SBoard.ViewTypeEnum>;
export type BoardColumnQueryParams = z.infer<typeof SBoard.BoardColumnQueryParams>;

export type BoardColumnResponse = z.infer<typeof SBoard.BoardColumn>;
export type BoardColumnListResponse = z.infer<typeof SBoard.BoardColumnListResponse>;
Expand Down
4 changes: 1 addition & 3 deletions src/entities/team/config/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import type { TeamRole } from '../model/types';

export const ROLE_LABELS: Record<Exclude<TeamRole, 'owner'>, string> = {
admin: 'Администратор',
lead: 'Лид',
moderator: 'Модератор',
member: 'Участник',
viewer: 'Гость',
} as const;

export const INVITATION_ROLES = [
...new Set<keyof typeof ROLE_LABELS>(['admin', 'lead', 'moderator', 'member', 'viewer']),
...new Set<keyof typeof ROLE_LABELS>(['admin', 'member', 'viewer']),
] as const;
8 changes: 5 additions & 3 deletions src/entities/team/config/statuses.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { MemberStatus } from '../model/types';

export const STATUS_LABELS: Record<MemberStatus, string> = {
export const STATUS_LABELS = {
active: 'Активен',
banned: 'Заблокирован',
blocked: 'Заблокирован',
inactive: 'Неактивен',
pending: 'Неактивен',
} as const;
} as const satisfies Record<MemberStatus, string>;

export const MEMBER_STATUSES = [
...new Set<keyof typeof STATUS_LABELS>(['active', 'pending', 'blocked']),
...new Set<keyof typeof STATUS_LABELS>(['active', 'inactive', 'banned']),
] as const;
12 changes: 6 additions & 6 deletions src/entities/team/model/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ export const TeamAvatarSchema = z
export const TeamRole = z.enum([
'owner',
'admin', // управление юзерами, настройками
'lead', // управление проектами
'moderator', // чистка контента/сообщений
'member', // обычный работяга
'viewer', // просто смотрит
]);

export const MemberStatus = z.enum([
'active', // Полноценный участник
'blocked', // Заблокирован не может вернуться по инвайту
'banned', // Заблокирован не может вернуться по инвайту
'inactive',
'blocked',
'pending',
]);

Expand Down Expand Up @@ -50,8 +50,8 @@ export const TeamDetailsResponse = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable(),
avatarUrl: z.string().nullable(),
coverUrl: z.string().nullable(),
avatar: TeamAvatarSchema,
cover: TeamAvatarSchema,
ownerId: z.string().nullable(),
createdAt: DateTimeString,
updatedAt: DateTimeString,
Expand All @@ -71,7 +71,7 @@ export const TeamInvitationResponse = z.object({
expiresAt: DateTimeString,
});

export const TeamInvitationListResponse = PaginatedResponseSchema(TeamInvitationResponse);
export const TeamInvitationListResponse = TeamInvitationResponse.array();

export const InviteMemberBody = z.object({
email: z.email(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ export function getDefaultCreateBoardColumnValues(position = 0): CreateBoardColu
return {
title: '',
color: DEFAULT_COLUMN_COLOR,
orderIndex: position,
position,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function useCreateBoardColumnForm(
const onSubmit = (data: CreateBoardColumnFormValues) => {
const body: TBoard.CreateBoardColumnBody = {
title: data.title,
orderIndex: data.orderIndex,
position: data.position,
...(data.color ? { color: data.color } : {}),
};

Expand Down
2 changes: 1 addition & 1 deletion src/pages/invitations/ui/InvitationsPageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { InvitationCard } from './InvitationCard';
export function InvitationsPageContent() {
const invitationsQuery = useSuspenseQuery(UserQueries.getMyInvitations());
const invitations = invitationsQuery.data.items;
const invitationsCount = invitationsQuery.data.meta.total ?? invitations.length;
const invitationsCount = invitationsQuery.data.items.length ?? 0;

return (
<PageWrapper
Expand Down
12 changes: 6 additions & 6 deletions src/pages/project/ui/boards/ProjectKanban.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const ProjectKanban = ({ data }: ProjectKanbanProps) => {

const ids = Object.keys(value);
const idsMap = new Map(ids.map((id, i) => [id, i]));
const columnIdsToUpdate: { columnId: string; orderIndex: number }[] = [];
const columnIdsToUpdate: { columnId: string; position: number }[] = [];

const prevColumns = queryClient.getQueryData<TBoard.BoardColumnListResponse>(
boardFabricKeys.columns(data.board.slug)
Expand All @@ -46,16 +46,16 @@ export const ProjectKanban = ({ data }: ProjectKanbanProps) => {
if (!oldColumns) return oldColumns;

return oldColumns.map((column) => {
const orderIndex = idsMap.get(column.id);
return orderIndex !== undefined ? { ...column, orderIndex } : column;
const position = idsMap.get(column.id);
return position !== undefined ? { ...column, position } : column;
});
}
);

prevColumns?.forEach((column) => {
const orderIndex = idsMap.get(column.id);
if (orderIndex !== undefined && column.orderIndex !== orderIndex) {
columnIdsToUpdate.push({ columnId: column.id, orderIndex });
const position = idsMap.get(column.id);
if (position !== undefined && column.position !== position) {
columnIdsToUpdate.push({ columnId: column.id, position });
}
});

Expand Down
Loading
Loading