diff --git a/src/app/api-utlities/@types/index.ts b/src/app/api-utlities/@types/index.ts
index d3d29da..dbf066b 100644
--- a/src/app/api-utlities/@types/index.ts
+++ b/src/app/api-utlities/@types/index.ts
@@ -67,6 +67,15 @@ export type TextResponse = Root<{
_znachenie: string | null;
}>;
+export type MediaResponse = Root<{
+ _name: string;
+ _foto: {
+ id: string;
+ url: string;
+ };
+ _klyuchNeIzmenya: string;
+}>;
+
export type Team = Root<{
_foto: Image[];
_zvanie: string;
diff --git a/src/app/api-utlities/presenters/index.ts b/src/app/api-utlities/presenters/index.ts
index a76aabe..88d829a 100644
--- a/src/app/api-utlities/presenters/index.ts
+++ b/src/app/api-utlities/presenters/index.ts
@@ -1,5 +1,7 @@
import { isEmpty } from 'lodash';
+import { MediaItem } from '@/shared/types/media.type';
+
import {
Certificate,
Charity,
@@ -7,6 +9,7 @@ import {
History,
Image,
Job,
+ MediaResponse,
Partner,
Review,
Select,
@@ -88,6 +91,17 @@ export const presentTexts = (texts: TextResponse) =>
value: item._znachenie,
}));
+export const presentMedia = (media: MediaResponse) => {
+ return media.records.map((record) => ({
+ key: record._klyuchNeIzmenya,
+ name: record._name,
+ photo: {
+ id: record._foto.id,
+ url: record._foto.url,
+ },
+ }));
+};
+
export const presentReviews = (reviews: Review) =>
reviews.records.map((review) => ({
id: review.id,
diff --git a/src/app/api-utlities/requests/common.ts b/src/app/api-utlities/requests/common.ts
index 136dd39..5b24ada 100644
--- a/src/app/api-utlities/requests/common.ts
+++ b/src/app/api-utlities/requests/common.ts
@@ -122,6 +122,19 @@ export const textsRequest = {
},
};
+export const mediaRequest = {
+ _mediaKontent: {
+ records: {
+ _name: true,
+ _foto: {
+ id: true,
+ url: true,
+ },
+ _klyuchNeIzmenya: true,
+ },
+ },
+};
+
export const teamRequest = {
_komanda: {
records: {
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 78cfcfc..7134cb9 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -2,8 +2,10 @@ import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import { textControlApi } from '@/shared/language/api/text-control.api';
+import { mediaControlApi } from '@/shared/media/api/media-control.api';
import { Providers } from '@/shared/providers/providers';
import { makeStore } from '@/shared/store';
+import { MediaItem } from '@/shared/types/media.type';
import { TextItem } from '@/shared/types/text.types';
import { Footer } from '@/widgets/footer';
@@ -29,10 +31,15 @@ export default async function RootLayout({
}>) {
const store = makeStore();
- const response = await store.dispatch(
+ // Запрос текстов
+ const textResponse = await store.dispatch(
textControlApi.endpoints.fetchText.initiate(),
);
+ // Запрос медиа
+ const mediaResponse = await store.dispatch(
+ mediaControlApi.endpoints.fetchMedia.initiate(),
+ );
return (
-
+
{children}
diff --git a/src/shared/language/hooks/use-text-controller.ts b/src/shared/language/hooks/use-text-controller.ts
index c519e2e..a387a9d 100644
--- a/src/shared/language/hooks/use-text-controller.ts
+++ b/src/shared/language/hooks/use-text-controller.ts
@@ -7,7 +7,9 @@ import { TextControlContext } from '../context/text-control-provider';
export function useTextController() {
const context = useContext(TextControlContext);
if (context === undefined) {
- throw new Error('useLanguage must be used within a LanguageProvider');
+ throw new Error(
+ 'useTextController must be used within a TextControlProvider',
+ );
}
if (typeof context.t !== 'function') {
diff --git a/src/shared/media/api/media-control.api.ts b/src/shared/media/api/media-control.api.ts
new file mode 100644
index 0000000..73b0f6b
--- /dev/null
+++ b/src/shared/media/api/media-control.api.ts
@@ -0,0 +1,24 @@
+import { jsonToGraphQLQuery } from 'json-to-graphql-query';
+
+import { presentMedia } from '@/app/api-utlities/presenters';
+import { mediaRequest } from '@/app/api-utlities/requests/common';
+
+import { taylorAPI } from '@/shared/api/taylor-api';
+import { MediaItem } from '@/shared/types/media.type';
+
+export const mediaControlApi = taylorAPI.injectEndpoints({
+ endpoints: (builder) => ({
+ fetchMedia: builder.query({
+ query: () => ({
+ url: '',
+ method: 'POST',
+ body: {
+ query: jsonToGraphQLQuery({ query: mediaRequest }),
+ },
+ }),
+ transformResponse: (response: any) => {
+ return presentMedia(response.data._mediaKontent);
+ },
+ }),
+ }),
+});
diff --git a/src/shared/media/context/media-control.provider.tsx b/src/shared/media/context/media-control.provider.tsx
new file mode 100644
index 0000000..d84841a
--- /dev/null
+++ b/src/shared/media/context/media-control.provider.tsx
@@ -0,0 +1,42 @@
+'use client';
+
+import { createContext, type ReactNode } from 'react';
+
+import { MediaItem } from '@/shared/types/media.type';
+
+export type MediaMap = Record;
+
+type MediaControlContextType = {
+ m: (key: string) => MediaItem | undefined;
+};
+
+export const MediaControlContext = createContext<
+ MediaControlContextType | undefined
+>(undefined);
+
+export function MediaControlProvider({
+ children,
+ mediaItems,
+}: {
+ children: ReactNode;
+ mediaItems?: MediaItem[];
+}) {
+ const mediaMap = mediaItems?.reduce((pr, cr) => {
+ pr[cr.key] = cr;
+ return pr;
+ }, {} as MediaMap);
+
+ const getMedia = (key: string): MediaItem | undefined => {
+ if (mediaMap?.[key]) {
+ return mediaMap[key];
+ }
+ console.warn(`Media key not found: ${key}`);
+ return undefined;
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/shared/media/hooks/use-media-controller.ts b/src/shared/media/hooks/use-media-controller.ts
new file mode 100644
index 0000000..ca7b6d3
--- /dev/null
+++ b/src/shared/media/hooks/use-media-controller.ts
@@ -0,0 +1,20 @@
+'use client';
+
+import { useContext } from 'react';
+
+import { MediaControlContext } from '../context/media-control.provider';
+
+export function useMediaController() {
+ const context = useContext(MediaControlContext);
+ if (context === undefined) {
+ throw new Error(
+ 'useMediaController must be used within a MediaControlProvider',
+ );
+ }
+
+ if (typeof context.m !== 'function') {
+ throw new Error('Media function (m) is not available');
+ }
+
+ return context;
+}
diff --git a/src/shared/providers/providers.tsx b/src/shared/providers/providers.tsx
index 3d7de22..9e8e768 100644
--- a/src/shared/providers/providers.tsx
+++ b/src/shared/providers/providers.tsx
@@ -4,8 +4,10 @@ import { TooltipProvider } from '@radix-ui/react-tooltip';
import { Provider } from 'react-redux';
import { TextControlProvider } from '../language';
+import { MediaControlProvider } from '../media/context/media-control.provider';
import { store } from '../store';
import { ThemeProvider } from '../theme/theme-provider';
+import { MediaItem } from '../types/media.type';
import { TextItem } from '../types/text.types';
import { AosProvider } from './aos-provider';
import { Toaster } from './toaster';
@@ -13,25 +15,32 @@ import { Toaster } from './toaster';
type ProvidersProps = {
children: React.ReactNode;
textItems: TextItem[];
+ mediaItems: MediaItem[];
};
-export const Providers = ({ children, textItems }: ProvidersProps) => {
+export const Providers = ({
+ children,
+ textItems,
+ mediaItems,
+}: ProvidersProps) => {
return (
-
-
-
- {children}
-
-
-
-
+
+
+
+
+ {children}
+
+
+
+
+
);
diff --git a/src/shared/types/media.type.ts b/src/shared/types/media.type.ts
new file mode 100644
index 0000000..5edd3c8
--- /dev/null
+++ b/src/shared/types/media.type.ts
@@ -0,0 +1,8 @@
+export interface MediaItem {
+ key: string; // _klyuchNeIzmenya
+ name: string; // _name
+ photo: {
+ id: string;
+ url: string;
+ };
+}
diff --git a/src/widgets/map-section.tsx b/src/widgets/map-section.tsx
index 1176227..dc42347 100644
--- a/src/widgets/map-section.tsx
+++ b/src/widgets/map-section.tsx
@@ -8,6 +8,7 @@ import { GasStationMap } from '@/features/map';
import { Container } from '@/shared/components/container';
import { useTextController } from '@/shared/language/hooks/use-text-controller';
+import { useMediaController } from '@/shared/media/hooks/use-media-controller';
interface MapSectionProps {
stations: Stations;
@@ -16,6 +17,11 @@ interface MapSectionProps {
export const MapSection = ({ stations }: MapSectionProps) => {
const { t } = useTextController();
+ const { m } = useMediaController();
+
+ // NOTE: doesn't work
+ const stationsMedia = m('stations.main');
+
return (