diff --git a/src/features/connect-textures-form/index.ts b/src/features/connect-textures-form/index.ts new file mode 100644 index 0000000..1631f27 --- /dev/null +++ b/src/features/connect-textures-form/index.ts @@ -0,0 +1 @@ +export { ConnectTexturesForm } from "@/features/connect-textures-form/ui/ConnectTexturesForm"; diff --git a/src/features/connect-textures-form/lib/static.ts b/src/features/connect-textures-form/lib/static.ts new file mode 100644 index 0000000..3f62959 --- /dev/null +++ b/src/features/connect-textures-form/lib/static.ts @@ -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; diff --git a/src/features/connect-textures-form/ui/ConnectTexturesForm.tsx b/src/features/connect-textures-form/ui/ConnectTexturesForm.tsx new file mode 100644 index 0000000..3e420bf --- /dev/null +++ b/src/features/connect-textures-form/ui/ConnectTexturesForm.tsx @@ -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 { + 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({ + 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 ( +
+
+ + ( + + Введите URL к сервису скинов + + + + {form.formState.errors.url_skins && ( + {form.formState.errors.url_skins.message} + )} + + )} + /> + + ( + + Введите URL к сервису плащей + + + + {form.formState.errors.url_cloaks && ( + {form.formState.errors.url_cloaks.message} + )} + + )} + /> + +
+ +
+ + +
+ ); +} diff --git a/src/screens/Integrations/ui/Integrations.tsx b/src/screens/Integrations/ui/Integrations.tsx index 300a841..5f8f5a6 100644 --- a/src/screens/Integrations/ui/Integrations.tsx +++ b/src/screens/Integrations/ui/Integrations.tsx @@ -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); @@ -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 ( @@ -42,6 +48,13 @@ export const IntegrationsPage = () => { description="Создайте лаунчер для платформ Windows, MacOS и Linux в пару кликов" action={onGenerateLauncherDialogToggle} /> + { open={isSentryConnectDialogOpen} onOpenChange={onSentryConnectDialogToggle} /> + + ); }; diff --git a/src/shared/api/contracts/integrations/requests.ts b/src/shared/api/contracts/integrations/requests.ts index e69ecce..1204607 100644 --- a/src/shared/api/contracts/integrations/requests.ts +++ b/src/shared/api/contracts/integrations/requests.ts @@ -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 = {}; @@ -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 & {}; diff --git a/src/shared/api/contracts/integrations/schemas.ts b/src/shared/api/contracts/integrations/schemas.ts index a2a66e3..9ca7928 100644 --- a/src/shared/api/contracts/integrations/schemas.ts +++ b/src/shared/api/contracts/integrations/schemas.ts @@ -11,3 +11,7 @@ export type BranchBaseEntity = { export type SentryBaseEntity = { url: string; }; + +export type TextureServiceBaseEntity = { + url: string; +}; diff --git a/src/shared/enums/index.ts b/src/shared/enums/index.ts index 91bab5b..b05dae7 100644 --- a/src/shared/enums/index.ts +++ b/src/shared/enums/index.ts @@ -1,2 +1,3 @@ -export * from './auth'; -export * from './systems'; +export * from "./auth"; +export * from "./systems"; +export * from "./textures"; diff --git a/src/shared/enums/textures.ts b/src/shared/enums/textures.ts new file mode 100644 index 0000000..285ac41 --- /dev/null +++ b/src/shared/enums/textures.ts @@ -0,0 +1,4 @@ +export enum TexturesServiceType { + TEXTURES_SERVICE_SKINS = "skins", + TEXTURES_SERVICE_CLOAKS = "cloaks", +} diff --git a/src/shared/hooks/useIntegraions.ts b/src/shared/hooks/useIntegraions.ts index feb49e2..fbedaa8 100644 --- a/src/shared/hooks/useIntegraions.ts +++ b/src/shared/hooks/useIntegraions.ts @@ -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({ @@ -86,7 +88,6 @@ export const useSentry = () => { export const useEditSentry = () => { const { toast } = useToast(); - const queryClient = useQueryClient(); return useMutation({ mutationKey: ["update-sentry"], @@ -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], + }); + } + }, + }); +}; diff --git a/src/shared/services/IntegrationService.ts b/src/shared/services/IntegrationService.ts index 6d97b3e..4c3edc2 100644 --- a/src/shared/services/IntegrationService.ts +++ b/src/shared/services/IntegrationService.ts @@ -6,11 +6,15 @@ import { TGetActiveAuthIntegrationsResponse, TGetAuthIntegrationsRequest, TGetAuthIntegrationsResponse, + TGetConnectTexturesRequest, + TGetConnectTexturesResponse, TGetInstallClientBranchesResponse, TGetSentryConnectRequest, TGetSentryConnectResponse, TPostAuthIntegrationsRequest, TPostAuthIntegrationsResponse, + TPutConnectTexturesRequest, + TPutConnectTexturesResponse, TPutSentryConnectRequest, TPutSentryConnectResponse, } from "@/shared/api/contracts"; @@ -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 { const { data } = await $api.get< @@ -66,6 +71,30 @@ class IntegrationService { return data; } + + async getConnectTextures({ + type, + ...params + }: TGetConnectTexturesRequest): Promise { + const { data } = await $api.get( + `${this.BASE_URL_TEXTURE}/${type}`, + { params }, + ); + + return data; + } + + async putConnectTextures({ + type, + ...body + }: TPutConnectTexturesRequest): Promise { + const { data } = await $api.put( + `${this.BASE_URL_TEXTURE}/${type}`, + body, + ); + + return data; + } } export const integrationService = new IntegrationService(); diff --git a/src/shared/ui/Breadcrumbs/index.ts b/src/shared/ui/Breadcrumbs/index.ts index e69de29..52c0da0 100644 --- a/src/shared/ui/Breadcrumbs/index.ts +++ b/src/shared/ui/Breadcrumbs/index.ts @@ -0,0 +1 @@ +export { Breadcrumbs } from "./ui/Breadcrumbs"; diff --git a/src/widgets/ConnectTexturesDialog/index.ts b/src/widgets/ConnectTexturesDialog/index.ts new file mode 100644 index 0000000..dbddbe5 --- /dev/null +++ b/src/widgets/ConnectTexturesDialog/index.ts @@ -0,0 +1 @@ +export { ConnectTexturesDialog } from "./ui/ConnectTexturesDialog"; diff --git a/src/widgets/ConnectTexturesDialog/ui/ConnectTexturesDialog.tsx b/src/widgets/ConnectTexturesDialog/ui/ConnectTexturesDialog.tsx new file mode 100644 index 0000000..b44ff1b --- /dev/null +++ b/src/widgets/ConnectTexturesDialog/ui/ConnectTexturesDialog.tsx @@ -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 ( + + + + Подключение сервиса скинов и плащей + + + + + ); +}