Skip to content

Commit

Permalink
GL-26: Добавлены интеграции с сервисом текстур (скины и плащи)
Browse files Browse the repository at this point in the history
  • Loading branch information
Терентьев Вадим Алексеевич committed May 4, 2024
1 parent 81fd28d commit e598931
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/features/connect-textures-form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ConnectTexturesForm } from "@/features/connect-textures-form/ui/ConnectTexturesForm";
14 changes: 14 additions & 0 deletions src/features/connect-textures-form/lib/static.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "zod";

export const ConnectTexturesSchema = z.object({
url_skins: z
.string()
.min(1, { message: "Вы не заполнили поле" })
.transform((v) => v.trim()),
url_cloaks: z
.string()
.min(1, { message: "Вы не заполнили поле" })
.transform((v) => v.trim()),
});

export type ConnectTexturesFormSchemaType = z.infer<typeof ConnectTexturesSchema>;
106 changes: 106 additions & 0 deletions src/features/connect-textures-form/ui/ConnectTexturesForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"use client";

import React from "react";

import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

import { cn } from "@/shared/lib/utils";
import { Button } from "@/shared/ui/button";
import { Form, FormControl, FormItem, FormLabel, FormMessage } from "@/shared/ui/form";
import { Input } from "@/shared/ui/input";
import { TexturesServiceType } from "@/shared/enums";
import { useConnectTextures, useEditConnectTextures } from "@/shared/hooks";
import { Icons } from "@/shared/ui/icons";

import { ConnectTexturesFormSchemaType, ConnectTexturesSchema } from "../lib/static";

interface ConnectTexturesFormProps extends React.HTMLAttributes<HTMLDivElement> {
onOpenChange: (open: boolean) => void;
}

export function ConnectTexturesForm({
className,
onOpenChange,
...props
}: ConnectTexturesFormProps) {
const { data: textures_skins } = useConnectTextures(TexturesServiceType.TEXTURES_SERVICE_SKINS);
const { data: textures_cloaks } = useConnectTextures(TexturesServiceType.TEXTURES_SERVICE_CLOAKS);

const { mutateAsync, isPending } = useEditConnectTextures();

const form = useForm<ConnectTexturesFormSchemaType>({
values: {
url_skins: textures_skins?.url || "",
url_cloaks: textures_cloaks?.url || "",
},
resolver: zodResolver(ConnectTexturesSchema),
});

const onSubmit = async (data: ConnectTexturesFormSchemaType) => {
if (form.formState.dirtyFields.url_skins) {
await mutateAsync({
type: TexturesServiceType.TEXTURES_SERVICE_SKINS,
url: data.url_skins,
}).then(() => {
onOpenChange(false);
});
}

if (form.formState.dirtyFields.url_cloaks) {
await mutateAsync({
type: TexturesServiceType.TEXTURES_SERVICE_CLOAKS,
url: data.url_cloaks,
}).then(() => {
onOpenChange(false);
});
}
};

return (
<div className={cn("grid gap-4", className)} {...props}>
<Form {...form}>
<form className="flex flex-col space-y-6" onSubmit={form.handleSubmit(onSubmit)}>
<Controller
control={form.control}
name="url_skins"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>Введите URL к сервису скинов</FormLabel>
<FormControl>
<Input placeholder="Введите URL к сервису скинов" {...field} />
</FormControl>
{form.formState.errors.url_skins && (
<FormMessage>{form.formState.errors.url_skins.message}</FormMessage>
)}
</FormItem>
)}
/>

<Controller
control={form.control}
name="url_cloaks"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>Введите URL к сервису плащей</FormLabel>
<FormControl>
<Input placeholder="Введите URL к сервису плащей" {...field} />
</FormControl>
{form.formState.errors.url_cloaks && (
<FormMessage>{form.formState.errors.url_cloaks.message}</FormMessage>
)}
</FormItem>
)}
/>

<div className="flex justify-between items-center">
<Button className="w-fit ml-auto" disabled={isPending || !form.formState.isDirty}>
{isPending && <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />}
Сохранить
</Button>
</div>
</form>
</Form>
</div>
);
}
20 changes: 19 additions & 1 deletion src/screens/Integrations/ui/Integrations.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"use client";

import { useState } from "react";

import { IntegrationCard } from "@/widgets/IntegrationCard";
import { GenerateLauncherDialog } from "@/widgets/GenerateLauncherDialog";
import { AuthenticationMethodDialog } from "@/widgets/AuthenticationMethodDialog";
import { SentryConnectDialog } from "@/widgets/SentryConnectDialog";
import { ConnectTexturesDialog } from "@/widgets/ConnectTexturesDialog";

import { Breadcrumbs } from "@/shared/ui/Breadcrumbs";
import { useSentry } from "@/shared/hooks";
import { DASHBOARD_PAGES } from "@/shared/routes";
import { Breadcrumbs } from "@/shared/ui/Breadcrumbs/ui/Breadcrumbs";

export const IntegrationsPage = () => {
const [isGenerateLauncherDialogOpen, setIsGenerateLauncherDialogOpen] = useState(false);
Expand All @@ -19,6 +22,9 @@ export const IntegrationsPage = () => {
const [isSentryConnectDialogOpen, setIsSentryConnectDialogOpen] = useState(false);
const onSentryConnectDialogToggle = () => setIsSentryConnectDialogOpen((prev) => !prev);

const [isConnectTexturesDialogOpen, setIsConnectTexturesDialogOpen] = useState(false);
const onConnectTexturesDialogToggle = () => setIsConnectTexturesDialogOpen((prev) => !prev);

const { data: sentry, isLoading: isLoadingSentry } = useSentry();

return (
Expand All @@ -42,6 +48,13 @@ export const IntegrationsPage = () => {
description="Создайте лаунчер для платформ Windows, MacOS и Linux в пару кликов"
action={onGenerateLauncherDialogToggle}
/>
<IntegrationCard
title="Сервис скинов"
description="Добавь интеграцию со сервисом скинов, для отображения скинов и плащей в игре"
action={onConnectTexturesDialogToggle}
status={"CONNECTED"}
buttonText={"Настроить"}
/>
<IntegrationCard
title="Sentry"
description={"Подключение платформы для отслеживания ошибок и мониторинга приложений"}
Expand Down Expand Up @@ -78,6 +91,11 @@ export const IntegrationsPage = () => {
open={isSentryConnectDialogOpen}
onOpenChange={onSentryConnectDialogToggle}
/>

<ConnectTexturesDialog
open={isConnectTexturesDialogOpen}
onOpenChange={onConnectTexturesDialogToggle}
/>
</>
);
};
12 changes: 12 additions & 0 deletions src/shared/api/contracts/integrations/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import {
AuthIntegrationBaseEntity,
BranchBaseEntity,
SentryBaseEntity,
TextureServiceBaseEntity,
} from "@/shared/api/contracts";
import { ResponseBaseEntity } from "@/shared/api/schemas";
import { TexturesServiceType } from "@/shared/enums";

// Получение списка серверов для авторизации
export type TGetAuthIntegrationsRequest = {};
Expand Down Expand Up @@ -39,3 +41,13 @@ export type TGetSentryConnectResponse = ResponseBaseEntity & {
// Изменение сервера авторизации
export type TPutSentryConnectRequest = { url: string };
export type TPutSentryConnectResponse = ResponseBaseEntity & {};

// Получение сервиса текстур
export type TGetConnectTexturesRequest = { type: TexturesServiceType };
export type TGetConnectTexturesResponse = ResponseBaseEntity & {
data: TextureServiceBaseEntity;
};

// Изменение сервиса текстур
export type TPutConnectTexturesRequest = { type: TexturesServiceType; url: string };
export type TPutConnectTexturesResponse = ResponseBaseEntity & {};
4 changes: 4 additions & 0 deletions src/shared/api/contracts/integrations/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ export type BranchBaseEntity = {
export type SentryBaseEntity = {
url: string;
};

export type TextureServiceBaseEntity = {
url: string;
};
5 changes: 3 additions & 2 deletions src/shared/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './auth';
export * from './systems';
export * from "./auth";
export * from "./systems";
export * from "./textures";
4 changes: 4 additions & 0 deletions src/shared/enums/textures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum TexturesServiceType {
TEXTURES_SERVICE_SKINS = "skins",
TEXTURES_SERVICE_CLOAKS = "cloaks",
}
35 changes: 34 additions & 1 deletion src/shared/hooks/useIntegraions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { isAxiosError } from "axios";
import {
AuthIntegrationBaseEntity,
TPostAuthIntegrationsRequest,
TPutConnectTexturesRequest,
TPutSentryConnectRequest,
} from "@/shared/api/contracts";
import { integrationService } from "@/shared/services/IntegrationService";
import { useToast } from "@/shared/ui/use-toast";
import { TexturesServiceType } from "@/shared/enums";

export const useCurrentIntegration = () => {
const { data } = useQuery<AuthIntegrationBaseEntity>({
Expand Down Expand Up @@ -86,7 +88,6 @@ export const useSentry = () => {

export const useEditSentry = () => {
const { toast } = useToast();
const queryClient = useQueryClient();

return useMutation({
mutationKey: ["update-sentry"],
Expand All @@ -108,3 +109,35 @@ export const useEditSentry = () => {
},
});
};

export const useConnectTextures = (type: TexturesServiceType) => {
return useQuery({
queryKey: [`get-connect-textures-${type}`],
queryFn: () => integrationService.getConnectTextures({ type }),
select: ({ data }) => data,
});
};

export const useEditConnectTextures = () => {
const { toast } = useToast();

return useMutation({
mutationKey: ["update-connect-textures"],
mutationFn: (data: TPutConnectTexturesRequest) => integrationService.putConnectTextures(data),
onSuccess: async (data) => {
toast({
title: "Успешно",
description: data.message,
});
},
onError: (error) => {
if (isAxiosError(error)) {
toast({
variant: "destructive",
title: (error.response && error.response.data.message) || "Ошибка!",
description: error.response && error.response.data.errors[0],
});
}
},
});
};
29 changes: 29 additions & 0 deletions src/shared/services/IntegrationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import {
TGetActiveAuthIntegrationsResponse,
TGetAuthIntegrationsRequest,
TGetAuthIntegrationsResponse,
TGetConnectTexturesRequest,
TGetConnectTexturesResponse,
TGetInstallClientBranchesResponse,
TGetSentryConnectRequest,
TGetSentryConnectResponse,
TPostAuthIntegrationsRequest,
TPostAuthIntegrationsResponse,
TPutConnectTexturesRequest,
TPutConnectTexturesResponse,
TPutSentryConnectRequest,
TPutSentryConnectResponse,
} from "@/shared/api/contracts";
Expand All @@ -20,6 +24,7 @@ class IntegrationService {
private BASE_URL_AUTH = `${this.BASE_URL}/auth`;
private BASE_URL_GITHUB = `${this.BASE_URL}/github`;
private BASE_URL_SENTRY = `${this.BASE_URL}/sentry/dsn`;
private BASE_URL_TEXTURE = `${this.BASE_URL}/texture`;

async getAuthIntegrations(): Promise<TGetAuthIntegrationsResponse> {
const { data } = await $api.get<
Expand Down Expand Up @@ -66,6 +71,30 @@ class IntegrationService {

return data;
}

async getConnectTextures({
type,
...params
}: TGetConnectTexturesRequest): Promise<TGetConnectTexturesResponse> {
const { data } = await $api.get<TGetConnectTexturesResponse>(
`${this.BASE_URL_TEXTURE}/${type}`,
{ params },
);

return data;
}

async putConnectTextures({
type,
...body
}: TPutConnectTexturesRequest): Promise<TPutConnectTexturesResponse> {
const { data } = await $api.put<TPutSentryConnectResponse>(
`${this.BASE_URL_TEXTURE}/${type}`,
body,
);

return data;
}
}

export const integrationService = new IntegrationService();
1 change: 1 addition & 0 deletions src/shared/ui/Breadcrumbs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Breadcrumbs } from "./ui/Breadcrumbs";
1 change: 1 addition & 0 deletions src/widgets/ConnectTexturesDialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ConnectTexturesDialog } from "./ui/ConnectTexturesDialog";
21 changes: 21 additions & 0 deletions src/widgets/ConnectTexturesDialog/ui/ConnectTexturesDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ConnectTexturesForm } from "@/features/connect-textures-form";

import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/shared/ui/dialog";

interface GenerateLauncherDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}

export function ConnectTexturesDialog(props: GenerateLauncherDialogProps) {
return (
<Dialog {...props}>
<DialogContent className="sm:max-w-[800px]">
<DialogHeader>
<DialogTitle>Подключение сервиса скинов и плащей</DialogTitle>
</DialogHeader>
<ConnectTexturesForm onOpenChange={props.onOpenChange} />
</DialogContent>
</Dialog>
);
}

0 comments on commit e598931

Please sign in to comment.