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
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
REACT_APP_BACKEND_URL=https://localhost:5001/api
REACT_APP_TEMPVAL=tempval
REACT_APP_API_URL=https://localhost:5001/api
REACT_APP_API_URL=https://localhost:5001/api

REACT_APP_GOOGLE_CLIENT_ID=
7 changes: 7 additions & 0 deletions config/webpack.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ module.exports = {
open: true,
port: "3000",
historyApiFallback: true,
client: {
// Показывать оверлеем только ошибки, без потока предупреждений.
overlay: {
errors: true,
warnings: false,
},
},
},
module: {
rules: require('./webpack.rules'),
Expand Down
17 changes: 16 additions & 1 deletion config/webpack.rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,22 @@ module.exports = [
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
{
loader: 'sass-loader',
options: {
// Заглушаем deprecation-предупреждения Dart Sass, которые
// флудят dev-сервер (по одному на каждый .scss-файл).
sassOptions: {
quietDeps: true, // тихо для зависимостей (node_modules, swiper)
silenceDeprecations: [
'legacy-js-api',
'import',
'global-builtin',
'color-functions',
],
},
},
},
],
}
].filter(Boolean);
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"private": true,
"dependencies": {
"@ant-design/icons": "^4.8.0",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@dnd-kit/utilities": "^3.2.2",
"@fortawesome/fontawesome-free": "^6.4.0",
"@react-google-maps/api": "^2.18.1",
"@react-hook/resize-observer": "^1.2.6",
Expand Down
9 changes: 5 additions & 4 deletions src/app/api/agent.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ axios.interceptors.response.use(
}
const failedUrl = response?.config?.url ?? config?.url ?? '';
switch (response?.status) {

case StatusCodes.INTERNAL_SERVER_ERROR:
errorMessage = failedUrl
? `${ReasonPhrases.INTERNAL_SERVER_ERROR}: ${failedUrl}`
Expand Down Expand Up @@ -72,22 +73,22 @@ axios.interceptors.response.use(
},
);

const responseBody = <T> (response: AxiosResponse<T>) => response.data;
const responseBody = <T>(response: AxiosResponse<T>) => response.data;

const Agent = {
get: async <T> (url: string, params?: URLSearchParams) => {
get: async <T>(url: string, params?: URLSearchParams) => {
axios.defaults.headers.common.Authorization = `Bearer ${UserLoginStore.getToken()}`;
return axios.get<T>(url, { params })
.then(responseBody);
},

post: async <T> (url: string, body: object, headers?: object) => {
post: async <T>(url: string, body: object, headers?: object) => {
axios.defaults.headers.common.Authorization = `Bearer ${UserLoginStore.getToken()}`;
return axios.post<T>(url, body, headers)
.then(responseBody);
},

put: async <T> (url: string, body: object) => {
put: async <T>(url: string, body: object) => {
axios.defaults.headers.common.Authorization = `Bearer ${UserLoginStore.getToken()}`;
return axios.put<T>(url, body)
.then(responseBody);
Expand Down
9 changes: 9 additions & 0 deletions src/app/api/media/art-slide-templates.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Agent from '@api/agent.api';
import { API_ROUTES } from '@constants/api-routes.constants';
import { ArtSlideTemplate } from '@models/media/art-slide-template.model';

const ArtSlideTemplatesApi = {
getAll: () => Agent.get<ArtSlideTemplate[]>(API_ROUTES.ART_SLIDE_TEMPLATES.GET_ALL),
};

export default ArtSlideTemplatesApi;
22 changes: 22 additions & 0 deletions src/app/api/media/art-slide.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Agent from '@api/agent.api';
import { API_ROUTES } from '@constants/api-routes.constants';
import { ArtSlide, CreateArtSlide, UpdateArtSlide } from '@models/media/art-slide.model';

const ArtSlidesApi = {
getAllByStreetcodeId: (streetcodeId: number) =>
Agent.get<ArtSlide[]>(`${API_ROUTES.ART_SLIDES.GET_BY_STREETCODE_ID}/${streetcodeId}`),

create: (artSlide: CreateArtSlide) =>
Agent.post<ArtSlide>(`${API_ROUTES.ART_SLIDES.CREATE}`, artSlide),

createAll: (artSlides: CreateArtSlide[]) =>
Agent.post<ArtSlide[]>(`${API_ROUTES.ART_SLIDES.CREATE_ALL}`, artSlides),

update: (artSlide: UpdateArtSlide) =>
Agent.put<ArtSlide>(`${API_ROUTES.ART_SLIDES.UPDATE}`, artSlide),

delete: (id: number) =>
Agent.delete(`${API_ROUTES.ART_SLIDES.DELETE}/${id}`),
};

export default ArtSlidesApi;
5 changes: 3 additions & 2 deletions src/app/api/media/arts.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ const ArtsApi = {

getById: (id: number) => Agent.get<Art>(`${API_ROUTES.ARTS.GET}/${id}`),

create: (art: Art) => Agent.post<Art>(`${API_ROUTES.ARTS.CREATE}`, art),
create: (data: FormData) => Agent.post<Art>(`${API_ROUTES.ARTS.CREATE}`, data),

update: (art: Art) => Agent.post<Art>(`${API_ROUTES.ARTS.UPDATE}`, art),
update: (id: number, data: { title: string; description: string }) =>
Agent.put<Art>(`${API_ROUTES.ARTS.UPDATE}/${id}`, data),

delete: (id: number) => Agent.delete(`${API_ROUTES.ARTS.DELETE}/${id}`),
};
Expand Down
28 changes: 28 additions & 0 deletions src/app/common/components/ImageTemplates-grid/TemplateRenderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { observer } from 'mobx-react-lite';
import { TEMPLATE_CLASS_MAP } from '@constants/template.map';
import './shared-grid.styles.scss';

export const TemplateRenderer = observer(({ template, renderSlot, className }: any) => {
const config = TEMPLATE_CLASS_MAP[template?.name];

const slots = config?.slots ?? [];
const gap = config?.gap ?? 5;
const templateClass = config?.className || 'template-default';

return (
<div
className={`preview-grid ${templateClass} ${className || ''}`}
style={{ gap: `${gap}px` }}
>
{slots.map((slot: any, index: number) => (
<div
key={slot.id}
className="preview-slot"
style={{ gridArea: `slot-${index}` }}
>
{renderSlot(slot)}
</div>
))}
</div>
);
});
100 changes: 100 additions & 0 deletions src/app/common/components/ImageTemplates-grid/shared-grid.styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
.preview-slot {
background: #000;
border-radius: 4px;
overflow: hidden;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;

img {
width: 100%;
height: 100%;
min-width: 100%;
min-height: 100%;
object-fit: cover;
object-position: center;
display: block;
}
}


.preview-grid {
position: relative;
display: grid;
gap: 5px;

// 1-2 слотф
&.template-single-large { grid-template-columns: 255px; grid-template-rows: 205px; grid-template-areas: "slot-0"; }
&.template-single-medium { grid-template-columns: 137px; grid-template-rows: 203px; grid-template-areas: "slot-0"; }
&.template-two-horizontal { grid-template-columns: 137px 137px; grid-template-rows: 203px; grid-template-areas: "slot-0 slot-1"; }

// 3 слота
&.template-three-mixed {
grid-template-columns: 137px 137px;
grid-template-rows: 203px 100px;
grid-template-areas: "slot-0 slot-1" "slot-2 slot-2";
}

// 4 слота
&.template-four-grid { grid-template-columns: repeat(2, 135px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-1" "slot-2 slot-3"; }
&.template-four-sidebar-left {
grid-template-columns: 135px 137px; grid-template-rows: 101px 101px;
grid-template-areas: "slot-0 slot-1" "slot-0 slot-2";
}
&.template-four-sidebar-right {
grid-template-columns: 137px 135px; grid-template-rows: 101px 101px;
grid-template-areas: "slot-1 slot-0" "slot-2 slot-0";
}

// 5 слотів
&.template-five-mixed { grid-template-columns: 137px 65px 65px; grid-template-rows: 101px 101px; grid-template-areas: "slot-0 slot-1 slot-2" "slot-0 slot-3 slot-4"; }
&.template-five-grid { grid-template-columns: repeat(3, 85px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-1 slot-2" "slot-3 slot-4 slot-4"; }
&.template-five-complex { grid-template-columns: repeat(2, 130px); grid-template-rows: repeat(3, 65px); grid-template-areas: "slot-0 slot-1" "slot-2 slot-3" "slot-4 slot-4"; }

// 6 слотів
&.template-six-grid { grid-template-columns: repeat(3, 85px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-1 slot-2" "slot-3 slot-4 slot-5"; }
&.template-six-compact { grid-template-columns: repeat(3, 85px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-1 slot-2" "slot-3 slot-4 slot-5"; }
&.template-six-mixed { grid-template-columns: repeat(3, 85px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-0 slot-1" "slot-2 slot-3 slot-4"; }
&.template-six-large { grid-template-columns: repeat(3, 85px); grid-template-rows: repeat(2, 101px); grid-template-areas: "slot-0 slot-1 slot-2" "slot-3 slot-4 slot-5"; }
}

.preview-grid.carousel-preview {
.preview-slot {
border-radius: 30px;
background: transparent;
}

/* 1 слот */
&.template-single-large { grid-template-columns: 380px; grid-template-rows: 305px; }
&.template-single-medium { grid-template-columns: 205px; grid-template-rows: 305px; }

/* 2 слота */
&.template-two-horizontal { grid-template-columns: repeat(2, 205px); grid-template-rows: 305px; }

/* 3 слота */
&.template-three-mixed { grid-template-columns: 205px 205px; grid-template-rows: 305px 150px; gap: 6px; }

/* 4 слота */
&.template-four-grid { grid-template-columns: repeat(2, 200px); grid-template-rows: repeat(2, 150px); gap: 6px; }
&.template-four-sidebar-left {
grid-template-columns: 200px 205px; grid-template-rows: 150px 150px; gap: 6px;
.preview-slot:first-child { grid-row: span 2; }
}
&.template-four-sidebar-right {
grid-template-columns: 205px 200px; grid-template-rows: 150px 150px; gap: 6px;
.preview-slot:first-child { grid-row: span 2; }
}

/* 5 слотів */
&.template-five-mixed { grid-template-columns: 205px 100px 100px; grid-template-rows: 150px 150px; gap: 6px; }
&.template-five-grid { grid-template-columns: repeat(3, 130px); grid-template-rows: repeat(2, 150px); gap: 6px; }
&.template-five-complex { grid-template-columns: repeat(2, 200px); grid-template-rows: repeat(3, 100px); gap: 6px; }

/* 6 слотів */
&.template-six-grid { grid-template-columns: repeat(3, 130px); grid-template-rows: repeat(2, 150px); gap: 6px; }
&.template-six-compact { grid-template-columns: repeat(3, 130px); grid-template-rows: repeat(2, 150px); gap: 6px; }
&.template-six-mixed { grid-template-columns: repeat(3, 130px); grid-template-rows: repeat(2, 150px); gap: 6px; }
&.template-six-large { grid-template-columns: repeat(3, 130px); grid-template-rows: repeat(2, 150px); gap: 6px; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { observer } from 'mobx-react-lite';
import { useModalContext } from '@stores/root-store';
import { Modal, Button } from 'antd';

const DeleteImageModal = observer(() => {
const { modalStore: { setModal, modalsState: { deleteImage } } } = useModalContext();

const onConfirm = () => {
deleteImage.image?.onConfirm?.();
setModal('deleteImage', undefined, false);
};
return (
<Modal
title="Видалити"
open={deleteImage.isOpen}
onOk={onConfirm}
onCancel={() => setModal('deleteImage')}
footer={[
<Button
key="back"
onClick={() => setModal('deleteImage', undefined, false)}
className="ant-btn-default"
>
Скасувати
</Button>,
<Button
key="submit"
type="primary"
danger
onClick={onConfirm}
className="ant-btn-primary"
>
Видалити
</Button>,
]}
>
<p>Ви впевнені?</p>
</Modal>
);
});

export default DeleteImageModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@use "@sass/variables/_variables.colors.scss" as c;
@use "@sass/mixins/_utils.mixins.scss" as mut;

.deleteModal {

.ant-btn-default {

color: #000000;

&:hover {
border-color: c.$accented-red-color;
color: c.$accented-red-color;
}
}

.ant-btn-primary {
background-color: c.$accented-red-color;
border-color: c.$accented-red-color;
color: c.$pure-white-color;

&:hover {
background-color: c.$dark-red-color;
border-color: c.$dark-red-color;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { observer } from 'mobx-react-lite';
import { useModalContext } from '@stores/root-store';
import { Modal, Button } from 'antd';

const DeleteImageTemplatesModal = observer(() => {
const { modalStore: { setModal, modalsState: { deleteImageTemplates } } } = useModalContext();

const onConfirm = () => {
deleteImageTemplates.image?.onConfirm?.();
setModal('deleteImageTemplates', undefined, false);
};

return (
<Modal
title="Видалити"
open={deleteImageTemplates.isOpen}
onOk={onConfirm}
onCancel={() => setModal('deleteImageTemplates', undefined, false)}
footer={[
<Button
key="back"
onClick={() => setModal('deleteImageTemplates', undefined, false)}
className="ant-btn-default"
>
Скасувати
</Button>,
<Button
key="submit"
type="primary"
danger
onClick={onConfirm}
className="ant-btn-primary"
>
Видалити
</Button>,
]}
>
<p>Ви впевнені, що хочете видалити цей елемент?</p>
</Modal>
);
});
export default DeleteImageTemplatesModal;
Loading