issues/27: content CRUD examples by /promotions
This commit is contained in:
committed by
Valeriy Petrov
parent
5aab178eb8
commit
90779fdc08
+36
@@ -21,6 +21,24 @@ import { EditStockPage } from './pages/EditStockPage';
|
||||
import { AddStockPage } from './pages/AddStockPage';
|
||||
import { InfoclinicListPage } from './pages/InfoclinicListPage';
|
||||
import { LostDoctorsPage } from './pages/LostDoctorsPage';
|
||||
import { NewsListPage } from './pages/NewsListPage';
|
||||
import { EditNewsPage } from './pages/EditNewsPage';
|
||||
import { AddNewsPage } from './pages/AddNewsPage';
|
||||
import { SitePromoListPage } from './pages/SitePromoListPage';
|
||||
import { EditSitePromoPage } from './pages/EditSitePromoPage';
|
||||
import { AddSitePromoPage } from './pages/AddSitePromoPage';
|
||||
import { DiseaseListPage } from './pages/DiseaseListPage';
|
||||
import { EditDiseasePage } from './pages/EditDiseasePage';
|
||||
import { AddDiseasePage } from './pages/AddDiseasePage';
|
||||
import { MedicalCenterListPage } from './pages/MedicalCenterListPage';
|
||||
import { EditMedicalCenterPage } from './pages/EditMedicalCenterPage';
|
||||
import { AddMedicalCenterPage } from './pages/AddMedicalCenterPage';
|
||||
import { ArticleListPage } from './pages/ArticleListPage';
|
||||
import { EditArticlePage } from './pages/EditArticlePage';
|
||||
import { AddArticlePage } from './pages/AddArticlePage';
|
||||
import { SiteServicesListPage } from './pages/SiteServicesListPage';
|
||||
import { EditSiteServicesPage } from './pages/EditSiteServicesPage';
|
||||
import { AddSiteServicesPage } from './pages/AddSiteServicesPage';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -51,6 +69,24 @@ function App() {
|
||||
<Route path="promotions" element={<StocksListPage/>} />
|
||||
<Route path="promotions/edit/:id" element={<EditStockPage/>} />
|
||||
<Route path="promotions/create" element={<AddStockPage/>} />
|
||||
<Route path="news" element={<NewsListPage />} />
|
||||
<Route path="news/edit/:id" element={<EditNewsPage />} />
|
||||
<Route path="news/create" element={<AddNewsPage />} />
|
||||
<Route path="site-promo" element={<SitePromoListPage />} />
|
||||
<Route path="site-promo/edit/:id" element={<EditSitePromoPage />} />
|
||||
<Route path="site-promo/create" element={<AddSitePromoPage />} />
|
||||
<Route path="disease" element={<DiseaseListPage />} />
|
||||
<Route path="disease/edit/:id" element={<EditDiseasePage />} />
|
||||
<Route path="disease/create" element={<AddDiseasePage />} />
|
||||
<Route path="medical-center" element={<MedicalCenterListPage />} />
|
||||
<Route path="medical-center/edit/:id" element={<EditMedicalCenterPage />} />
|
||||
<Route path="medical-center/create" element={<AddMedicalCenterPage />} />
|
||||
<Route path="article" element={<ArticleListPage />} />
|
||||
<Route path="article/edit/:id" element={<EditArticlePage />} />
|
||||
<Route path="article/create" element={<AddArticlePage />} />
|
||||
<Route path="site-services" element={<SiteServicesListPage />} />
|
||||
<Route path="site-services/edit/:id" element={<EditSiteServicesPage />} />
|
||||
<Route path="site-services/create" element={<AddSiteServicesPage />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const articleApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getArticleList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&limit=20`;
|
||||
else queryString += `limit=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/article/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getArticle: build.query({
|
||||
query: ({ articleId }) => ({
|
||||
url: `/article/${articleId}`,
|
||||
}),
|
||||
}),
|
||||
createArticle: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/article/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateArticle: build.mutation({
|
||||
query: ({ articleId, data }) => ({
|
||||
url: `/article/${articleId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteArticle: build.mutation({
|
||||
query: ({ articleId }) => ({
|
||||
url: `/article/${articleId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetArticleQuery,
|
||||
useGetArticleListQuery,
|
||||
useCreateArticleMutation,
|
||||
useUpdateArticleMutation,
|
||||
useDeleteArticleMutation,
|
||||
} = articleApi;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const diseaseApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getDiseaseList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&perPage=20`;
|
||||
else queryString += `perPage=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/disease/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getDisease: build.query({
|
||||
query: ({ diseaseId }) => ({
|
||||
url: `/disease/${diseaseId}`,
|
||||
}),
|
||||
}),
|
||||
createDisease: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/disease/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateDisease: build.mutation({
|
||||
query: ({ diseaseId, data }) => ({
|
||||
url: `/disease/${diseaseId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteDisease: build.mutation({
|
||||
query: ({ diseaseId }) => ({
|
||||
url: `/disease/${diseaseId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetDiseaseQuery,
|
||||
useGetDiseaseListQuery,
|
||||
useCreateDiseaseMutation,
|
||||
useUpdateDiseaseMutation,
|
||||
useDeleteDiseaseMutation,
|
||||
} = diseaseApi;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const medicalCenterApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getMedicalCenterList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&perPage=20`;
|
||||
else queryString += `perPage=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/medical-center/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getMedicalCenter: build.query({
|
||||
query: ({ medicalCenterId }) => ({
|
||||
url: `/medical-center/${medicalCenterId}`,
|
||||
}),
|
||||
}),
|
||||
createMedicalCenter: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/medical-center/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateMedicalCenter: build.mutation({
|
||||
query: ({ medicalCenterId, data }) => ({
|
||||
url: `/medical-center/${medicalCenterId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteMedicalCenter: build.mutation({
|
||||
query: ({ medicalCenterId }) => ({
|
||||
url: `/medical-center/${medicalCenterId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetMedicalCenterQuery,
|
||||
useGetMedicalCenterListQuery,
|
||||
useCreateMedicalCenterMutation,
|
||||
useUpdateMedicalCenterMutation,
|
||||
useDeleteMedicalCenterMutation,
|
||||
} = medicalCenterApi;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const newsApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getNewsList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&perPage=20`;
|
||||
else queryString += `perPage=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/news/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getNews: build.query({
|
||||
query: ({ newsId }) => ({
|
||||
url: `/news/${newsId}`,
|
||||
}),
|
||||
}),
|
||||
createNews: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/news/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateNews: build.mutation({
|
||||
query: ({ newsId, data }) => ({
|
||||
url: `/news/${newsId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteNews: build.mutation({
|
||||
query: ({ newsId }) => ({
|
||||
url: `/news/${newsId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetNewsQuery,
|
||||
useGetNewsListQuery,
|
||||
useCreateNewsMutation,
|
||||
useUpdateNewsMutation,
|
||||
useDeleteNewsMutation,
|
||||
} = newsApi;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const promoApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getSitePromoList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&perPage=20`;
|
||||
else queryString += `perPage=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/promo/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getSitePromo: build.query({
|
||||
query: ({ promoId }) => ({
|
||||
url: `/promo/${promoId}`,
|
||||
}),
|
||||
}),
|
||||
createSitePromo: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/promo/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateSitePromo: build.mutation({
|
||||
query: ({ promoId, data }) => ({
|
||||
url: `/promo/${promoId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteSitePromo: build.mutation({
|
||||
query: ({ promoId }) => ({
|
||||
url: `/promo/${promoId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetSitePromoQuery,
|
||||
useGetSitePromoListQuery,
|
||||
useCreateSitePromoMutation,
|
||||
useUpdateSitePromoMutation,
|
||||
useDeleteSitePromoMutation,
|
||||
} = promoApi;
|
||||
@@ -0,0 +1,55 @@
|
||||
import { API, authHeader } from './apiSlice';
|
||||
|
||||
export const siteServicesApi = API.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
getSiteServicesList: build.query({
|
||||
query: ({ search = '', page = '' }) => {
|
||||
let queryString = '?';
|
||||
if (page) queryString += `page=${page}&perPage=20`;
|
||||
else queryString += `perPage=20`;
|
||||
if (search) queryString += `&search=${encodeURIComponent(search)}`;
|
||||
return {
|
||||
url: `/site-services/list${queryString}`,
|
||||
};
|
||||
},
|
||||
refetchOnMountOrArgChange: true,
|
||||
keepUnusedDataFor: 0,
|
||||
}),
|
||||
getSiteServices: build.query({
|
||||
query: ({ siteServicesId }) => ({
|
||||
url: `/site-services/${siteServicesId}`,
|
||||
}),
|
||||
}),
|
||||
createSiteServices: build.mutation({
|
||||
query: ({ data }) => ({
|
||||
url: `/site-services/create`,
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
updateSiteServices: build.mutation({
|
||||
query: ({ siteServicesId, data }) => ({
|
||||
url: `/site-services/${siteServicesId}`,
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
}),
|
||||
deleteSiteServices: build.mutation({
|
||||
query: ({ siteServicesId }) => ({
|
||||
url: `/site-services/${siteServicesId}`,
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetSiteServicesQuery,
|
||||
useGetSiteServicesListQuery,
|
||||
useCreateSiteServicesMutation,
|
||||
useUpdateSiteServicesMutation,
|
||||
useDeleteSiteServicesMutation,
|
||||
} = siteServicesApi;
|
||||
@@ -15,6 +15,12 @@ export const Navbar = () => {
|
||||
{ to: '/promotions',icon: 'fas fa-percent', label: 'Акции' },
|
||||
{ to: '/departments',icon: 'fas fa-stethoscope', label: 'Отделения' },
|
||||
{ to: '/filials',icon: 'fas fa-building', label: 'Филиалы' },
|
||||
{ to: '/news', icon: 'fas fa-newspaper', label: 'Новости' },
|
||||
{ to: '/site-promo', icon: 'fas fa-bullhorn', label: 'Промо (контент)' },
|
||||
{ to: '/disease', icon: 'fas fa-heartbeat', label: 'Заболевания' },
|
||||
{ to: '/medical-center', icon: 'fas fa-hospital', label: 'Медцентры' },
|
||||
{ to: '/article', icon: 'fas fa-file-alt', label: 'Статьи' },
|
||||
{ to: '/site-services', icon: 'fas fa-concierge-bell', label: 'Услуги сайта' },
|
||||
];
|
||||
const [open, setOpen] = useState(false);
|
||||
const toggleRef = useRef(null);
|
||||
|
||||
@@ -10,6 +10,12 @@ export const Sidebar = () => {
|
||||
{ to: '/promotions',icon: 'fas fa-percent', label: 'Акции' },
|
||||
{ to: '/departments',icon: 'fas fa-stethoscope', label: 'Отделения' },
|
||||
{ to: '/filials',icon: 'fas fa-building', label: 'Филиалы' },
|
||||
{ to: '/news', icon: 'fas fa-newspaper', label: 'Новости' },
|
||||
{ to: '/site-promo', icon: 'fas fa-bullhorn', label: 'Промо (контент)' },
|
||||
{ to: '/disease', icon: 'fas fa-heartbeat', label: 'Заболевания' },
|
||||
{ to: '/medical-center', icon: 'fas fa-hospital', label: 'Медцентры' },
|
||||
{ to: '/article', icon: 'fas fa-file-alt', label: 'Статьи' },
|
||||
{ to: '/site-services', icon: 'fas fa-concierge-bell', label: 'Услуги сайта' },
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateArticleMutation } from '/src/api/apiArticle';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddArticlePage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/article`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createArticle] = useCreateArticleMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewPicture: '',
|
||||
doctors: '',
|
||||
services: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewPicture = form.previewPicture === '' ? null : form.previewPicture;
|
||||
data.doctors = !form.doctors || !String(form.doctors).trim() ? null : JSON.parse(form.doctors);
|
||||
data.services = !form.services || !String(form.services).trim() ? null : JSON.parse(form.services);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createArticle({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/article/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: статью`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewPicture</label><input type="text" className="form-control" value={form.previewPicture} onChange={handleChange('previewPicture')} /></div>
|
||||
<div className="form-group"><label>doctors (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.doctors} onChange={handleChange('doctors')} /></div>
|
||||
<div className="form-group"><label>services (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.services} onChange={handleChange('services')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateDiseaseMutation } from '/src/api/apiDisease';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddDiseasePage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/disease`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createDisease] = useCreateDiseaseMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewPicture: '',
|
||||
hidePicture: false,
|
||||
readTime: '',
|
||||
diseasesName: '',
|
||||
diseasesOtherName: '',
|
||||
symptom: '',
|
||||
staff: '',
|
||||
bibliography: '',
|
||||
tagsImportant: '',
|
||||
tags: '',
|
||||
linkServices: '',
|
||||
staffList: '',
|
||||
staffPost: '',
|
||||
staffPostExclude: '',
|
||||
linkFaq: '',
|
||||
staffCheck: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewPicture = form.previewPicture === '' ? null : form.previewPicture;
|
||||
data.hidePicture = Boolean(form.hidePicture);
|
||||
data.readTime = form.readTime === '' ? null : form.readTime;
|
||||
data.diseasesName = form.diseasesName === '' ? null : form.diseasesName;
|
||||
data.diseasesOtherName = form.diseasesOtherName === '' ? null : form.diseasesOtherName;
|
||||
data.symptom = form.symptom === '' ? null : form.symptom;
|
||||
data.staff = form.staff === '' ? null : form.staff;
|
||||
data.bibliography = form.bibliography === '' ? null : form.bibliography;
|
||||
data.tagsImportant = !form.tagsImportant || !String(form.tagsImportant).trim() ? null : JSON.parse(form.tagsImportant);
|
||||
data.tags = !form.tags || !String(form.tags).trim() ? null : JSON.parse(form.tags);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.staffList = !form.staffList || !String(form.staffList).trim() ? null : JSON.parse(form.staffList);
|
||||
data.staffPost = !form.staffPost || !String(form.staffPost).trim() ? null : JSON.parse(form.staffPost);
|
||||
data.staffPostExclude = !form.staffPostExclude || !String(form.staffPostExclude).trim() ? null : JSON.parse(form.staffPostExclude);
|
||||
data.linkFaq = !form.linkFaq || !String(form.linkFaq).trim() ? null : JSON.parse(form.linkFaq);
|
||||
data.staffCheck = !form.staffCheck || !String(form.staffCheck).trim() ? null : JSON.parse(form.staffCheck);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createDisease({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/disease/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: заболевание`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewPicture</label><input type="text" className="form-control" value={form.previewPicture} onChange={handleChange('previewPicture')} /></div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="hidePicture" checked={Boolean(form.hidePicture)} onChange={handleChange('hidePicture')} /><label className="form-check-label" htmlFor="hidePicture">hidePicture</label></div>
|
||||
<div className="form-group"><label>readTime</label><input type="text" className="form-control" value={form.readTime} onChange={handleChange('readTime')} /></div>
|
||||
<div className="form-group"><label>diseasesName</label><input type="text" className="form-control" value={form.diseasesName} onChange={handleChange('diseasesName')} /></div>
|
||||
<div className="form-group"><label>diseasesOtherName</label><input type="text" className="form-control" value={form.diseasesOtherName} onChange={handleChange('diseasesOtherName')} /></div>
|
||||
<div className="form-group"><label>symptom</label><input type="text" className="form-control" value={form.symptom} onChange={handleChange('symptom')} /></div>
|
||||
<div className="form-group"><label>staff</label><input type="text" className="form-control" value={form.staff} onChange={handleChange('staff')} /></div>
|
||||
<div className="form-group"><label>bibliography</label><input type="text" className="form-control" value={form.bibliography} onChange={handleChange('bibliography')} /></div>
|
||||
<div className="form-group"><label>tagsImportant (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tagsImportant} onChange={handleChange('tagsImportant')} /></div>
|
||||
<div className="form-group"><label>tags (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tags} onChange={handleChange('tags')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>staffList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffList} onChange={handleChange('staffList')} /></div>
|
||||
<div className="form-group"><label>staffPost (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffPost} onChange={handleChange('staffPost')} /></div>
|
||||
<div className="form-group"><label>staffPostExclude (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffPostExclude} onChange={handleChange('staffPostExclude')} /></div>
|
||||
<div className="form-group"><label>linkFaq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkFaq} onChange={handleChange('linkFaq')} /></div>
|
||||
<div className="form-group"><label>staffCheck (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffCheck} onChange={handleChange('staffCheck')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateMedicalCenterMutation } from '/src/api/apiMedicalCenter';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddMedicalCenterPage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/medical-center`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createMedicalCenter] = useCreateMedicalCenterMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
mainLinkStaff: '',
|
||||
plusText: '',
|
||||
plusTitle: '',
|
||||
processText: '',
|
||||
processTitle: '',
|
||||
servicesTitle: '',
|
||||
trainingText: '',
|
||||
trainingTextTitle: '',
|
||||
whyText: '',
|
||||
whyTitle: '',
|
||||
hidePicture: '',
|
||||
kodUslug: '',
|
||||
doctors: '',
|
||||
services: '',
|
||||
articles: '',
|
||||
txtUp: '',
|
||||
contraindications: '',
|
||||
indications: '',
|
||||
linkSale: '',
|
||||
plusList: '',
|
||||
servicesList: '',
|
||||
servicesPhotos: '',
|
||||
sortStaff: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.mainLinkStaff = form.mainLinkStaff === '' ? null : form.mainLinkStaff;
|
||||
data.plusText = form.plusText === '' ? null : form.plusText;
|
||||
data.plusTitle = form.plusTitle === '' ? null : form.plusTitle;
|
||||
data.processText = form.processText === '' ? null : form.processText;
|
||||
data.processTitle = form.processTitle === '' ? null : form.processTitle;
|
||||
data.servicesTitle = form.servicesTitle === '' ? null : form.servicesTitle;
|
||||
data.trainingText = form.trainingText === '' ? null : form.trainingText;
|
||||
data.trainingTextTitle = form.trainingTextTitle === '' ? null : form.trainingTextTitle;
|
||||
data.whyText = form.whyText === '' ? null : form.whyText;
|
||||
data.whyTitle = form.whyTitle === '' ? null : form.whyTitle;
|
||||
data.hidePicture = form.hidePicture === '' ? null : Number(form.hidePicture);
|
||||
data.kodUslug = !form.kodUslug || !String(form.kodUslug).trim() ? null : JSON.parse(form.kodUslug);
|
||||
data.doctors = !form.doctors || !String(form.doctors).trim() ? null : JSON.parse(form.doctors);
|
||||
data.services = !form.services || !String(form.services).trim() ? null : JSON.parse(form.services);
|
||||
data.articles = !form.articles || !String(form.articles).trim() ? null : JSON.parse(form.articles);
|
||||
data.txtUp = !form.txtUp || !String(form.txtUp).trim() ? null : JSON.parse(form.txtUp);
|
||||
data.contraindications = !form.contraindications || !String(form.contraindications).trim() ? null : JSON.parse(form.contraindications);
|
||||
data.indications = !form.indications || !String(form.indications).trim() ? null : JSON.parse(form.indications);
|
||||
data.linkSale = !form.linkSale || !String(form.linkSale).trim() ? null : JSON.parse(form.linkSale);
|
||||
data.plusList = !form.plusList || !String(form.plusList).trim() ? null : JSON.parse(form.plusList);
|
||||
data.servicesList = !form.servicesList || !String(form.servicesList).trim() ? null : JSON.parse(form.servicesList);
|
||||
data.servicesPhotos = !form.servicesPhotos || !String(form.servicesPhotos).trim() ? null : JSON.parse(form.servicesPhotos);
|
||||
data.sortStaff = !form.sortStaff || !String(form.sortStaff).trim() ? null : JSON.parse(form.sortStaff);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createMedicalCenter({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/medical-center/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: медцентр`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>mainLinkStaff</label><input type="text" className="form-control" value={form.mainLinkStaff} onChange={handleChange('mainLinkStaff')} /></div>
|
||||
<div className="form-group"><label>plusText</label><input type="text" className="form-control" value={form.plusText} onChange={handleChange('plusText')} /></div>
|
||||
<div className="form-group"><label>plusTitle</label><input type="text" className="form-control" value={form.plusTitle} onChange={handleChange('plusTitle')} /></div>
|
||||
<div className="form-group"><label>processText</label><input type="text" className="form-control" value={form.processText} onChange={handleChange('processText')} /></div>
|
||||
<div className="form-group"><label>processTitle</label><input type="text" className="form-control" value={form.processTitle} onChange={handleChange('processTitle')} /></div>
|
||||
<div className="form-group"><label>servicesTitle</label><input type="text" className="form-control" value={form.servicesTitle} onChange={handleChange('servicesTitle')} /></div>
|
||||
<div className="form-group"><label>trainingText</label><input type="text" className="form-control" value={form.trainingText} onChange={handleChange('trainingText')} /></div>
|
||||
<div className="form-group"><label>trainingTextTitle</label><input type="text" className="form-control" value={form.trainingTextTitle} onChange={handleChange('trainingTextTitle')} /></div>
|
||||
<div className="form-group"><label>whyText</label><input type="text" className="form-control" value={form.whyText} onChange={handleChange('whyText')} /></div>
|
||||
<div className="form-group"><label>whyTitle</label><input type="text" className="form-control" value={form.whyTitle} onChange={handleChange('whyTitle')} /></div>
|
||||
<div className="form-group"><label>hidePicture</label><input type="number" className="form-control" value={form.hidePicture} onChange={handleChange('hidePicture')} /></div>
|
||||
<div className="form-group"><label>kodUslug (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.kodUslug} onChange={handleChange('kodUslug')} /></div>
|
||||
<div className="form-group"><label>doctors (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.doctors} onChange={handleChange('doctors')} /></div>
|
||||
<div className="form-group"><label>services (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.services} onChange={handleChange('services')} /></div>
|
||||
<div className="form-group"><label>articles (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.articles} onChange={handleChange('articles')} /></div>
|
||||
<div className="form-group"><label>txtUp (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.txtUp} onChange={handleChange('txtUp')} /></div>
|
||||
<div className="form-group"><label>contraindications (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.contraindications} onChange={handleChange('contraindications')} /></div>
|
||||
<div className="form-group"><label>indications (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.indications} onChange={handleChange('indications')} /></div>
|
||||
<div className="form-group"><label>linkSale (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkSale} onChange={handleChange('linkSale')} /></div>
|
||||
<div className="form-group"><label>plusList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.plusList} onChange={handleChange('plusList')} /></div>
|
||||
<div className="form-group"><label>servicesList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesList} onChange={handleChange('servicesList')} /></div>
|
||||
<div className="form-group"><label>servicesPhotos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesPhotos} onChange={handleChange('servicesPhotos')} /></div>
|
||||
<div className="form-group"><label>sortStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.sortStaff} onChange={handleChange('sortStaff')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateNewsMutation } from '/src/api/apiNews';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddNewsPage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/news`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createNews] = useCreateNewsMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
shortName: '',
|
||||
linkElPrice: '',
|
||||
timer: '',
|
||||
timerBg: '',
|
||||
formOrder: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.shortName = form.shortName === '' ? null : form.shortName;
|
||||
data.linkElPrice = form.linkElPrice === '' ? null : form.linkElPrice;
|
||||
data.timer = form.timer === '' ? null : form.timer;
|
||||
data.timerBg = form.timerBg === '' ? null : form.timerBg;
|
||||
data.formOrder = !form.formOrder || !String(form.formOrder).trim() ? null : JSON.parse(form.formOrder);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createNews({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/news/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: новость`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>Короткое название</label><input type="text" className="form-control" value={form.shortName} onChange={handleChange('shortName')} /></div>
|
||||
<div className="form-group"><label>Ссылка на прайс</label><input type="text" className="form-control" value={form.linkElPrice} onChange={handleChange('linkElPrice')} /></div>
|
||||
<div className="form-group"><label>Таймер</label><input type="text" className="form-control" value={form.timer} onChange={handleChange('timer')} /></div>
|
||||
<div className="form-group"><label>Фон таймера</label><input type="text" className="form-control" value={form.timerBg} onChange={handleChange('timerBg')} /></div>
|
||||
<div className="form-group"><label>formOrder (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.formOrder} onChange={handleChange('formOrder')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateSitePromoMutation } from '/src/api/apiSitePromo';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddSitePromoPage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/site-promo`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createSitePromo] = useCreateSitePromoMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
shortName: '',
|
||||
period: '',
|
||||
timer: '',
|
||||
timerBg: '',
|
||||
clinics: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.shortName = form.shortName === '' ? null : form.shortName;
|
||||
data.period = form.period === '' ? null : form.period;
|
||||
data.timer = form.timer === '' ? null : form.timer;
|
||||
data.timerBg = form.timerBg === '' ? null : form.timerBg;
|
||||
data.clinics = !form.clinics || !String(form.clinics).trim() ? null : JSON.parse(form.clinics);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createSitePromo({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/site-promo/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: промо`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>Короткое название</label><input type="text" className="form-control" value={form.shortName} onChange={handleChange('shortName')} /></div>
|
||||
<div className="form-group"><label>Период</label><input type="text" className="form-control" value={form.period} onChange={handleChange('period')} /></div>
|
||||
<div className="form-group"><label>Таймер</label><input type="text" className="form-control" value={form.timer} onChange={handleChange('timer')} /></div>
|
||||
<div className="form-group"><label>Фон таймера</label><input type="text" className="form-control" value={form.timerBg} onChange={handleChange('timerBg')} /></div>
|
||||
<div className="form-group"><label>clinics (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.clinics} onChange={handleChange('clinics')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useCreateSiteServicesMutation } from '/src/api/apiSiteServices';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function AddSiteServicesPage() {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/site-services`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const [createSiteServices] = useCreateSiteServicesMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewImg: '',
|
||||
partPrice: '',
|
||||
pokazaniya: '',
|
||||
preparation: '',
|
||||
protivopokazaniya: '',
|
||||
bannerImg: '',
|
||||
bannerImgM: '',
|
||||
bannerImgUrl: '',
|
||||
downloadFile: '',
|
||||
fullWidthBanner: '',
|
||||
kodUslug: '',
|
||||
linkPrice: '',
|
||||
photosTitle: '',
|
||||
contraindicationsList: '',
|
||||
customBlockText: '',
|
||||
customBlockText2: '',
|
||||
customBlockTitle: '',
|
||||
customBlockTitle2: '',
|
||||
indicationsList: '',
|
||||
plusList: '',
|
||||
plusText: '',
|
||||
plusTitle: '',
|
||||
prepareTitle: '',
|
||||
processText: '',
|
||||
processTitle: '',
|
||||
servicesList: '',
|
||||
servicesTitle: '',
|
||||
textUp: '',
|
||||
trainingText: '',
|
||||
whyText: '',
|
||||
whyTitle: '',
|
||||
hidePicture: '',
|
||||
linkVideoreviews: '',
|
||||
faq: '',
|
||||
hideSignBtn: '',
|
||||
quiz: '',
|
||||
tags: '',
|
||||
tagsImportant: '',
|
||||
clinics: '',
|
||||
staffUp: '',
|
||||
advantages: '',
|
||||
saleId: '',
|
||||
sortStaff: '',
|
||||
linkArticlesServices: '',
|
||||
servicesPhotos: '',
|
||||
linkFaq: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewImg = form.previewImg === '' ? null : form.previewImg;
|
||||
data.partPrice = form.partPrice === '' ? null : form.partPrice;
|
||||
data.pokazaniya = form.pokazaniya === '' ? null : form.pokazaniya;
|
||||
data.preparation = form.preparation === '' ? null : form.preparation;
|
||||
data.protivopokazaniya = form.protivopokazaniya === '' ? null : form.protivopokazaniya;
|
||||
data.bannerImg = form.bannerImg === '' ? null : form.bannerImg;
|
||||
data.bannerImgM = form.bannerImgM === '' ? null : form.bannerImgM;
|
||||
data.bannerImgUrl = form.bannerImgUrl === '' ? null : form.bannerImgUrl;
|
||||
data.downloadFile = form.downloadFile === '' ? null : form.downloadFile;
|
||||
data.fullWidthBanner = form.fullWidthBanner === '' ? null : form.fullWidthBanner;
|
||||
data.kodUslug = form.kodUslug === '' ? null : form.kodUslug;
|
||||
data.linkPrice = form.linkPrice === '' ? null : form.linkPrice;
|
||||
data.photosTitle = form.photosTitle === '' ? null : form.photosTitle;
|
||||
data.contraindicationsList = form.contraindicationsList === '' ? null : form.contraindicationsList;
|
||||
data.customBlockText = form.customBlockText === '' ? null : form.customBlockText;
|
||||
data.customBlockText2 = form.customBlockText2 === '' ? null : form.customBlockText2;
|
||||
data.customBlockTitle = form.customBlockTitle === '' ? null : form.customBlockTitle;
|
||||
data.customBlockTitle2 = form.customBlockTitle2 === '' ? null : form.customBlockTitle2;
|
||||
data.indicationsList = form.indicationsList === '' ? null : form.indicationsList;
|
||||
data.plusList = form.plusList === '' ? null : form.plusList;
|
||||
data.plusText = form.plusText === '' ? null : form.plusText;
|
||||
data.plusTitle = form.plusTitle === '' ? null : form.plusTitle;
|
||||
data.prepareTitle = form.prepareTitle === '' ? null : form.prepareTitle;
|
||||
data.processText = form.processText === '' ? null : form.processText;
|
||||
data.processTitle = form.processTitle === '' ? null : form.processTitle;
|
||||
data.servicesList = form.servicesList === '' ? null : form.servicesList;
|
||||
data.servicesTitle = form.servicesTitle === '' ? null : form.servicesTitle;
|
||||
data.textUp = form.textUp === '' ? null : form.textUp;
|
||||
data.trainingText = form.trainingText === '' ? null : form.trainingText;
|
||||
data.whyText = form.whyText === '' ? null : form.whyText;
|
||||
data.whyTitle = form.whyTitle === '' ? null : form.whyTitle;
|
||||
data.hidePicture = form.hidePicture === '' ? null : Number(form.hidePicture);
|
||||
data.linkVideoreviews = !form.linkVideoreviews || !String(form.linkVideoreviews).trim() ? null : JSON.parse(form.linkVideoreviews);
|
||||
data.faq = !form.faq || !String(form.faq).trim() ? null : JSON.parse(form.faq);
|
||||
data.hideSignBtn = !form.hideSignBtn || !String(form.hideSignBtn).trim() ? null : JSON.parse(form.hideSignBtn);
|
||||
data.quiz = !form.quiz || !String(form.quiz).trim() ? null : JSON.parse(form.quiz);
|
||||
data.tags = !form.tags || !String(form.tags).trim() ? null : JSON.parse(form.tags);
|
||||
data.tagsImportant = !form.tagsImportant || !String(form.tagsImportant).trim() ? null : JSON.parse(form.tagsImportant);
|
||||
data.clinics = !form.clinics || !String(form.clinics).trim() ? null : JSON.parse(form.clinics);
|
||||
data.staffUp = !form.staffUp || !String(form.staffUp).trim() ? null : JSON.parse(form.staffUp);
|
||||
data.advantages = !form.advantages || !String(form.advantages).trim() ? null : JSON.parse(form.advantages);
|
||||
data.saleId = !form.saleId || !String(form.saleId).trim() ? null : JSON.parse(form.saleId);
|
||||
data.sortStaff = !form.sortStaff || !String(form.sortStaff).trim() ? null : JSON.parse(form.sortStaff);
|
||||
data.linkArticlesServices = !form.linkArticlesServices || !String(form.linkArticlesServices).trim() ? null : JSON.parse(form.linkArticlesServices);
|
||||
data.servicesPhotos = !form.servicesPhotos || !String(form.servicesPhotos).trim() ? null : JSON.parse(form.servicesPhotos);
|
||||
data.linkFaq = !form.linkFaq || !String(form.linkFaq).trim() ? null : JSON.parse(form.linkFaq);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
const response = await createSiteServices({ data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => { navigate(`/site-services/edit/${response.id}`); window.location.reload(); }, 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Добавление: услугу`} handleSave={handleSave} handleDelete={() => {}} isAddSpecialist={true}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewImg</label><input type="text" className="form-control" value={form.previewImg} onChange={handleChange('previewImg')} /></div>
|
||||
<div className="form-group"><label>partPrice</label><input type="text" className="form-control" value={form.partPrice} onChange={handleChange('partPrice')} /></div>
|
||||
<div className="form-group"><label>pokazaniya</label><input type="text" className="form-control" value={form.pokazaniya} onChange={handleChange('pokazaniya')} /></div>
|
||||
<div className="form-group"><label>preparation</label><input type="text" className="form-control" value={form.preparation} onChange={handleChange('preparation')} /></div>
|
||||
<div className="form-group"><label>protivopokazaniya</label><input type="text" className="form-control" value={form.protivopokazaniya} onChange={handleChange('protivopokazaniya')} /></div>
|
||||
<div className="form-group"><label>bannerImg</label><input type="text" className="form-control" value={form.bannerImg} onChange={handleChange('bannerImg')} /></div>
|
||||
<div className="form-group"><label>bannerImgM</label><input type="text" className="form-control" value={form.bannerImgM} onChange={handleChange('bannerImgM')} /></div>
|
||||
<div className="form-group"><label>bannerImgUrl</label><input type="text" className="form-control" value={form.bannerImgUrl} onChange={handleChange('bannerImgUrl')} /></div>
|
||||
<div className="form-group"><label>downloadFile</label><input type="text" className="form-control" value={form.downloadFile} onChange={handleChange('downloadFile')} /></div>
|
||||
<div className="form-group"><label>fullWidthBanner</label><input type="text" className="form-control" value={form.fullWidthBanner} onChange={handleChange('fullWidthBanner')} /></div>
|
||||
<div className="form-group"><label>kodUslug</label><input type="text" className="form-control" value={form.kodUslug} onChange={handleChange('kodUslug')} /></div>
|
||||
<div className="form-group"><label>linkPrice</label><input type="text" className="form-control" value={form.linkPrice} onChange={handleChange('linkPrice')} /></div>
|
||||
<div className="form-group"><label>photosTitle</label><input type="text" className="form-control" value={form.photosTitle} onChange={handleChange('photosTitle')} /></div>
|
||||
<div className="form-group"><label>contraindicationsList</label><input type="text" className="form-control" value={form.contraindicationsList} onChange={handleChange('contraindicationsList')} /></div>
|
||||
<div className="form-group"><label>customBlockText</label><input type="text" className="form-control" value={form.customBlockText} onChange={handleChange('customBlockText')} /></div>
|
||||
<div className="form-group"><label>customBlockText2</label><input type="text" className="form-control" value={form.customBlockText2} onChange={handleChange('customBlockText2')} /></div>
|
||||
<div className="form-group"><label>customBlockTitle</label><input type="text" className="form-control" value={form.customBlockTitle} onChange={handleChange('customBlockTitle')} /></div>
|
||||
<div className="form-group"><label>customBlockTitle2</label><input type="text" className="form-control" value={form.customBlockTitle2} onChange={handleChange('customBlockTitle2')} /></div>
|
||||
<div className="form-group"><label>indicationsList</label><input type="text" className="form-control" value={form.indicationsList} onChange={handleChange('indicationsList')} /></div>
|
||||
<div className="form-group"><label>plusList</label><input type="text" className="form-control" value={form.plusList} onChange={handleChange('plusList')} /></div>
|
||||
<div className="form-group"><label>plusText</label><input type="text" className="form-control" value={form.plusText} onChange={handleChange('plusText')} /></div>
|
||||
<div className="form-group"><label>plusTitle</label><input type="text" className="form-control" value={form.plusTitle} onChange={handleChange('plusTitle')} /></div>
|
||||
<div className="form-group"><label>prepareTitle</label><input type="text" className="form-control" value={form.prepareTitle} onChange={handleChange('prepareTitle')} /></div>
|
||||
<div className="form-group"><label>processText</label><input type="text" className="form-control" value={form.processText} onChange={handleChange('processText')} /></div>
|
||||
<div className="form-group"><label>processTitle</label><input type="text" className="form-control" value={form.processTitle} onChange={handleChange('processTitle')} /></div>
|
||||
<div className="form-group"><label>servicesList</label><input type="text" className="form-control" value={form.servicesList} onChange={handleChange('servicesList')} /></div>
|
||||
<div className="form-group"><label>servicesTitle</label><input type="text" className="form-control" value={form.servicesTitle} onChange={handleChange('servicesTitle')} /></div>
|
||||
<div className="form-group"><label>textUp</label><input type="text" className="form-control" value={form.textUp} onChange={handleChange('textUp')} /></div>
|
||||
<div className="form-group"><label>trainingText</label><input type="text" className="form-control" value={form.trainingText} onChange={handleChange('trainingText')} /></div>
|
||||
<div className="form-group"><label>whyText</label><input type="text" className="form-control" value={form.whyText} onChange={handleChange('whyText')} /></div>
|
||||
<div className="form-group"><label>whyTitle</label><input type="text" className="form-control" value={form.whyTitle} onChange={handleChange('whyTitle')} /></div>
|
||||
<div className="form-group"><label>hidePicture</label><input type="number" className="form-control" value={form.hidePicture} onChange={handleChange('hidePicture')} /></div>
|
||||
<div className="form-group"><label>linkVideoreviews (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkVideoreviews} onChange={handleChange('linkVideoreviews')} /></div>
|
||||
<div className="form-group"><label>faq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.faq} onChange={handleChange('faq')} /></div>
|
||||
<div className="form-group"><label>hideSignBtn (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.hideSignBtn} onChange={handleChange('hideSignBtn')} /></div>
|
||||
<div className="form-group"><label>quiz (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.quiz} onChange={handleChange('quiz')} /></div>
|
||||
<div className="form-group"><label>tags (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tags} onChange={handleChange('tags')} /></div>
|
||||
<div className="form-group"><label>tagsImportant (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tagsImportant} onChange={handleChange('tagsImportant')} /></div>
|
||||
<div className="form-group"><label>clinics (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.clinics} onChange={handleChange('clinics')} /></div>
|
||||
<div className="form-group"><label>staffUp (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffUp} onChange={handleChange('staffUp')} /></div>
|
||||
<div className="form-group"><label>advantages (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.advantages} onChange={handleChange('advantages')} /></div>
|
||||
<div className="form-group"><label>saleId (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.saleId} onChange={handleChange('saleId')} /></div>
|
||||
<div className="form-group"><label>sortStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.sortStaff} onChange={handleChange('sortStaff')} /></div>
|
||||
<div className="form-group"><label>linkArticlesServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkArticlesServices} onChange={handleChange('linkArticlesServices')} /></div>
|
||||
<div className="form-group"><label>servicesPhotos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesPhotos} onChange={handleChange('servicesPhotos')} /></div>
|
||||
<div className="form-group"><label>linkFaq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkFaq} onChange={handleChange('linkFaq')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetArticleListQuery } from '/src/api/apiArticle';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const ArticleListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetArticleListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination
|
||||
? response.pagination
|
||||
: response.meta
|
||||
? {
|
||||
total_pages: response.meta.totalPages ?? 1,
|
||||
current_page: response.meta.page ?? 1,
|
||||
has_previous_page: (response.meta.page ?? 1) > 1,
|
||||
has_next_page: (response.meta.page ?? 1) < (response.meta.totalPages ?? 1),
|
||||
}
|
||||
: {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Статьи</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/article/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 4 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/article/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetDiseaseListQuery } from '/src/api/apiDisease';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const DiseaseListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetDiseaseListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination || {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Заболевания</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/disease/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 4 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/disease/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetArticleQuery, useUpdateArticleMutation, useDeleteArticleMutation } from '/src/api/apiArticle';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditArticlePage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/article`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetArticleQuery({ articleId: id });
|
||||
const [updateArticle] = useUpdateArticleMutation();
|
||||
const [deleteArticle] = useDeleteArticleMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewPicture: '',
|
||||
doctors: '',
|
||||
services: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
previewPicture: item.previewPicture ?? '',
|
||||
doctors: item.doctors == null ? '' : JSON.stringify(item.doctors, null, 2),
|
||||
services: item.services == null ? '' : JSON.stringify(item.services, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteArticle({ articleId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewPicture = form.previewPicture === '' ? null : form.previewPicture;
|
||||
data.doctors = !form.doctors || !String(form.doctors).trim() ? null : JSON.parse(form.doctors);
|
||||
data.services = !form.services || !String(form.services).trim() ? null : JSON.parse(form.services);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateArticle({ articleId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Статья с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: статью #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewPicture</label><input type="text" className="form-control" value={form.previewPicture} onChange={handleChange('previewPicture')} /></div>
|
||||
<div className="form-group"><label>doctors (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.doctors} onChange={handleChange('doctors')} /></div>
|
||||
<div className="form-group"><label>services (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.services} onChange={handleChange('services')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetDiseaseQuery, useUpdateDiseaseMutation, useDeleteDiseaseMutation } from '/src/api/apiDisease';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditDiseasePage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/disease`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetDiseaseQuery({ diseaseId: id });
|
||||
const [updateDisease] = useUpdateDiseaseMutation();
|
||||
const [deleteDisease] = useDeleteDiseaseMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewPicture: '',
|
||||
hidePicture: false,
|
||||
readTime: '',
|
||||
diseasesName: '',
|
||||
diseasesOtherName: '',
|
||||
symptom: '',
|
||||
staff: '',
|
||||
bibliography: '',
|
||||
tagsImportant: '',
|
||||
tags: '',
|
||||
linkServices: '',
|
||||
staffList: '',
|
||||
staffPost: '',
|
||||
staffPostExclude: '',
|
||||
linkFaq: '',
|
||||
staffCheck: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
previewPicture: item.previewPicture ?? '',
|
||||
hidePicture: Boolean(item.hidePicture),
|
||||
readTime: item.readTime ?? '',
|
||||
diseasesName: item.diseasesName ?? '',
|
||||
diseasesOtherName: item.diseasesOtherName ?? '',
|
||||
symptom: item.symptom ?? '',
|
||||
staff: item.staff ?? '',
|
||||
bibliography: item.bibliography ?? '',
|
||||
tagsImportant: item.tagsImportant == null ? '' : JSON.stringify(item.tagsImportant, null, 2),
|
||||
tags: item.tags == null ? '' : JSON.stringify(item.tags, null, 2),
|
||||
linkServices: item.linkServices == null ? '' : JSON.stringify(item.linkServices, null, 2),
|
||||
staffList: item.staffList == null ? '' : JSON.stringify(item.staffList, null, 2),
|
||||
staffPost: item.staffPost == null ? '' : JSON.stringify(item.staffPost, null, 2),
|
||||
staffPostExclude: item.staffPostExclude == null ? '' : JSON.stringify(item.staffPostExclude, null, 2),
|
||||
linkFaq: item.linkFaq == null ? '' : JSON.stringify(item.linkFaq, null, 2),
|
||||
staffCheck: item.staffCheck == null ? '' : JSON.stringify(item.staffCheck, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteDisease({ diseaseId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewPicture = form.previewPicture === '' ? null : form.previewPicture;
|
||||
data.hidePicture = Boolean(form.hidePicture);
|
||||
data.readTime = form.readTime === '' ? null : form.readTime;
|
||||
data.diseasesName = form.diseasesName === '' ? null : form.diseasesName;
|
||||
data.diseasesOtherName = form.diseasesOtherName === '' ? null : form.diseasesOtherName;
|
||||
data.symptom = form.symptom === '' ? null : form.symptom;
|
||||
data.staff = form.staff === '' ? null : form.staff;
|
||||
data.bibliography = form.bibliography === '' ? null : form.bibliography;
|
||||
data.tagsImportant = !form.tagsImportant || !String(form.tagsImportant).trim() ? null : JSON.parse(form.tagsImportant);
|
||||
data.tags = !form.tags || !String(form.tags).trim() ? null : JSON.parse(form.tags);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.staffList = !form.staffList || !String(form.staffList).trim() ? null : JSON.parse(form.staffList);
|
||||
data.staffPost = !form.staffPost || !String(form.staffPost).trim() ? null : JSON.parse(form.staffPost);
|
||||
data.staffPostExclude = !form.staffPostExclude || !String(form.staffPostExclude).trim() ? null : JSON.parse(form.staffPostExclude);
|
||||
data.linkFaq = !form.linkFaq || !String(form.linkFaq).trim() ? null : JSON.parse(form.linkFaq);
|
||||
data.staffCheck = !form.staffCheck || !String(form.staffCheck).trim() ? null : JSON.parse(form.staffCheck);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateDisease({ diseaseId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Заболевание с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: заболевание #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewPicture</label><input type="text" className="form-control" value={form.previewPicture} onChange={handleChange('previewPicture')} /></div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="hidePicture" checked={Boolean(form.hidePicture)} onChange={handleChange('hidePicture')} /><label className="form-check-label" htmlFor="hidePicture">hidePicture</label></div>
|
||||
<div className="form-group"><label>readTime</label><input type="text" className="form-control" value={form.readTime} onChange={handleChange('readTime')} /></div>
|
||||
<div className="form-group"><label>diseasesName</label><input type="text" className="form-control" value={form.diseasesName} onChange={handleChange('diseasesName')} /></div>
|
||||
<div className="form-group"><label>diseasesOtherName</label><input type="text" className="form-control" value={form.diseasesOtherName} onChange={handleChange('diseasesOtherName')} /></div>
|
||||
<div className="form-group"><label>symptom</label><input type="text" className="form-control" value={form.symptom} onChange={handleChange('symptom')} /></div>
|
||||
<div className="form-group"><label>staff</label><input type="text" className="form-control" value={form.staff} onChange={handleChange('staff')} /></div>
|
||||
<div className="form-group"><label>bibliography</label><input type="text" className="form-control" value={form.bibliography} onChange={handleChange('bibliography')} /></div>
|
||||
<div className="form-group"><label>tagsImportant (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tagsImportant} onChange={handleChange('tagsImportant')} /></div>
|
||||
<div className="form-group"><label>tags (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tags} onChange={handleChange('tags')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>staffList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffList} onChange={handleChange('staffList')} /></div>
|
||||
<div className="form-group"><label>staffPost (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffPost} onChange={handleChange('staffPost')} /></div>
|
||||
<div className="form-group"><label>staffPostExclude (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffPostExclude} onChange={handleChange('staffPostExclude')} /></div>
|
||||
<div className="form-group"><label>linkFaq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkFaq} onChange={handleChange('linkFaq')} /></div>
|
||||
<div className="form-group"><label>staffCheck (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffCheck} onChange={handleChange('staffCheck')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetMedicalCenterQuery, useUpdateMedicalCenterMutation, useDeleteMedicalCenterMutation } from '/src/api/apiMedicalCenter';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditMedicalCenterPage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/medical-center`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetMedicalCenterQuery({ medicalCenterId: id });
|
||||
const [updateMedicalCenter] = useUpdateMedicalCenterMutation();
|
||||
const [deleteMedicalCenter] = useDeleteMedicalCenterMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
mainLinkStaff: '',
|
||||
plusText: '',
|
||||
plusTitle: '',
|
||||
processText: '',
|
||||
processTitle: '',
|
||||
servicesTitle: '',
|
||||
trainingText: '',
|
||||
trainingTextTitle: '',
|
||||
whyText: '',
|
||||
whyTitle: '',
|
||||
hidePicture: '',
|
||||
kodUslug: '',
|
||||
doctors: '',
|
||||
services: '',
|
||||
articles: '',
|
||||
txtUp: '',
|
||||
contraindications: '',
|
||||
indications: '',
|
||||
linkSale: '',
|
||||
plusList: '',
|
||||
servicesList: '',
|
||||
servicesPhotos: '',
|
||||
sortStaff: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
mainLinkStaff: item.mainLinkStaff ?? '',
|
||||
plusText: item.plusText ?? '',
|
||||
plusTitle: item.plusTitle ?? '',
|
||||
processText: item.processText ?? '',
|
||||
processTitle: item.processTitle ?? '',
|
||||
servicesTitle: item.servicesTitle ?? '',
|
||||
trainingText: item.trainingText ?? '',
|
||||
trainingTextTitle: item.trainingTextTitle ?? '',
|
||||
whyText: item.whyText ?? '',
|
||||
whyTitle: item.whyTitle ?? '',
|
||||
hidePicture: item.hidePicture ?? '',
|
||||
kodUslug: item.kodUslug == null ? '' : JSON.stringify(item.kodUslug, null, 2),
|
||||
doctors: item.doctors == null ? '' : JSON.stringify(item.doctors, null, 2),
|
||||
services: item.services == null ? '' : JSON.stringify(item.services, null, 2),
|
||||
articles: item.articles == null ? '' : JSON.stringify(item.articles, null, 2),
|
||||
txtUp: item.txtUp == null ? '' : JSON.stringify(item.txtUp, null, 2),
|
||||
contraindications: item.contraindications == null ? '' : JSON.stringify(item.contraindications, null, 2),
|
||||
indications: item.indications == null ? '' : JSON.stringify(item.indications, null, 2),
|
||||
linkSale: item.linkSale == null ? '' : JSON.stringify(item.linkSale, null, 2),
|
||||
plusList: item.plusList == null ? '' : JSON.stringify(item.plusList, null, 2),
|
||||
servicesList: item.servicesList == null ? '' : JSON.stringify(item.servicesList, null, 2),
|
||||
servicesPhotos: item.servicesPhotos == null ? '' : JSON.stringify(item.servicesPhotos, null, 2),
|
||||
sortStaff: item.sortStaff == null ? '' : JSON.stringify(item.sortStaff, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteMedicalCenter({ medicalCenterId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.mainLinkStaff = form.mainLinkStaff === '' ? null : form.mainLinkStaff;
|
||||
data.plusText = form.plusText === '' ? null : form.plusText;
|
||||
data.plusTitle = form.plusTitle === '' ? null : form.plusTitle;
|
||||
data.processText = form.processText === '' ? null : form.processText;
|
||||
data.processTitle = form.processTitle === '' ? null : form.processTitle;
|
||||
data.servicesTitle = form.servicesTitle === '' ? null : form.servicesTitle;
|
||||
data.trainingText = form.trainingText === '' ? null : form.trainingText;
|
||||
data.trainingTextTitle = form.trainingTextTitle === '' ? null : form.trainingTextTitle;
|
||||
data.whyText = form.whyText === '' ? null : form.whyText;
|
||||
data.whyTitle = form.whyTitle === '' ? null : form.whyTitle;
|
||||
data.hidePicture = form.hidePicture === '' ? null : Number(form.hidePicture);
|
||||
data.kodUslug = !form.kodUslug || !String(form.kodUslug).trim() ? null : JSON.parse(form.kodUslug);
|
||||
data.doctors = !form.doctors || !String(form.doctors).trim() ? null : JSON.parse(form.doctors);
|
||||
data.services = !form.services || !String(form.services).trim() ? null : JSON.parse(form.services);
|
||||
data.articles = !form.articles || !String(form.articles).trim() ? null : JSON.parse(form.articles);
|
||||
data.txtUp = !form.txtUp || !String(form.txtUp).trim() ? null : JSON.parse(form.txtUp);
|
||||
data.contraindications = !form.contraindications || !String(form.contraindications).trim() ? null : JSON.parse(form.contraindications);
|
||||
data.indications = !form.indications || !String(form.indications).trim() ? null : JSON.parse(form.indications);
|
||||
data.linkSale = !form.linkSale || !String(form.linkSale).trim() ? null : JSON.parse(form.linkSale);
|
||||
data.plusList = !form.plusList || !String(form.plusList).trim() ? null : JSON.parse(form.plusList);
|
||||
data.servicesList = !form.servicesList || !String(form.servicesList).trim() ? null : JSON.parse(form.servicesList);
|
||||
data.servicesPhotos = !form.servicesPhotos || !String(form.servicesPhotos).trim() ? null : JSON.parse(form.servicesPhotos);
|
||||
data.sortStaff = !form.sortStaff || !String(form.sortStaff).trim() ? null : JSON.parse(form.sortStaff);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateMedicalCenter({ medicalCenterId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Медцентр с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: медцентр #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>mainLinkStaff</label><input type="text" className="form-control" value={form.mainLinkStaff} onChange={handleChange('mainLinkStaff')} /></div>
|
||||
<div className="form-group"><label>plusText</label><input type="text" className="form-control" value={form.plusText} onChange={handleChange('plusText')} /></div>
|
||||
<div className="form-group"><label>plusTitle</label><input type="text" className="form-control" value={form.plusTitle} onChange={handleChange('plusTitle')} /></div>
|
||||
<div className="form-group"><label>processText</label><input type="text" className="form-control" value={form.processText} onChange={handleChange('processText')} /></div>
|
||||
<div className="form-group"><label>processTitle</label><input type="text" className="form-control" value={form.processTitle} onChange={handleChange('processTitle')} /></div>
|
||||
<div className="form-group"><label>servicesTitle</label><input type="text" className="form-control" value={form.servicesTitle} onChange={handleChange('servicesTitle')} /></div>
|
||||
<div className="form-group"><label>trainingText</label><input type="text" className="form-control" value={form.trainingText} onChange={handleChange('trainingText')} /></div>
|
||||
<div className="form-group"><label>trainingTextTitle</label><input type="text" className="form-control" value={form.trainingTextTitle} onChange={handleChange('trainingTextTitle')} /></div>
|
||||
<div className="form-group"><label>whyText</label><input type="text" className="form-control" value={form.whyText} onChange={handleChange('whyText')} /></div>
|
||||
<div className="form-group"><label>whyTitle</label><input type="text" className="form-control" value={form.whyTitle} onChange={handleChange('whyTitle')} /></div>
|
||||
<div className="form-group"><label>hidePicture</label><input type="number" className="form-control" value={form.hidePicture} onChange={handleChange('hidePicture')} /></div>
|
||||
<div className="form-group"><label>kodUslug (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.kodUslug} onChange={handleChange('kodUslug')} /></div>
|
||||
<div className="form-group"><label>doctors (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.doctors} onChange={handleChange('doctors')} /></div>
|
||||
<div className="form-group"><label>services (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.services} onChange={handleChange('services')} /></div>
|
||||
<div className="form-group"><label>articles (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.articles} onChange={handleChange('articles')} /></div>
|
||||
<div className="form-group"><label>txtUp (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.txtUp} onChange={handleChange('txtUp')} /></div>
|
||||
<div className="form-group"><label>contraindications (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.contraindications} onChange={handleChange('contraindications')} /></div>
|
||||
<div className="form-group"><label>indications (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.indications} onChange={handleChange('indications')} /></div>
|
||||
<div className="form-group"><label>linkSale (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkSale} onChange={handleChange('linkSale')} /></div>
|
||||
<div className="form-group"><label>plusList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.plusList} onChange={handleChange('plusList')} /></div>
|
||||
<div className="form-group"><label>servicesList (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesList} onChange={handleChange('servicesList')} /></div>
|
||||
<div className="form-group"><label>servicesPhotos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesPhotos} onChange={handleChange('servicesPhotos')} /></div>
|
||||
<div className="form-group"><label>sortStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.sortStaff} onChange={handleChange('sortStaff')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetNewsQuery, useUpdateNewsMutation, useDeleteNewsMutation } from '/src/api/apiNews';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditNewsPage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/news`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetNewsQuery({ newsId: id });
|
||||
const [updateNews] = useUpdateNewsMutation();
|
||||
const [deleteNews] = useDeleteNewsMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
shortName: '',
|
||||
linkElPrice: '',
|
||||
timer: '',
|
||||
timerBg: '',
|
||||
formOrder: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
shortName: item.shortName ?? '',
|
||||
linkElPrice: item.linkElPrice ?? '',
|
||||
timer: item.timer ?? '',
|
||||
timerBg: item.timerBg ?? '',
|
||||
formOrder: item.formOrder == null ? '' : JSON.stringify(item.formOrder, null, 2),
|
||||
linkServices: item.linkServices == null ? '' : JSON.stringify(item.linkServices, null, 2),
|
||||
linkStaff: item.linkStaff == null ? '' : JSON.stringify(item.linkStaff, null, 2),
|
||||
photos: item.photos == null ? '' : JSON.stringify(item.photos, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteNews({ newsId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.shortName = form.shortName === '' ? null : form.shortName;
|
||||
data.linkElPrice = form.linkElPrice === '' ? null : form.linkElPrice;
|
||||
data.timer = form.timer === '' ? null : form.timer;
|
||||
data.timerBg = form.timerBg === '' ? null : form.timerBg;
|
||||
data.formOrder = !form.formOrder || !String(form.formOrder).trim() ? null : JSON.parse(form.formOrder);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateNews({ newsId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Новость с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: новость #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>Короткое название</label><input type="text" className="form-control" value={form.shortName} onChange={handleChange('shortName')} /></div>
|
||||
<div className="form-group"><label>Ссылка на прайс</label><input type="text" className="form-control" value={form.linkElPrice} onChange={handleChange('linkElPrice')} /></div>
|
||||
<div className="form-group"><label>Таймер</label><input type="text" className="form-control" value={form.timer} onChange={handleChange('timer')} /></div>
|
||||
<div className="form-group"><label>Фон таймера</label><input type="text" className="form-control" value={form.timerBg} onChange={handleChange('timerBg')} /></div>
|
||||
<div className="form-group"><label>formOrder (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.formOrder} onChange={handleChange('formOrder')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetSitePromoQuery, useUpdateSitePromoMutation, useDeleteSitePromoMutation } from '/src/api/apiSitePromo';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditSitePromoPage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/site-promo`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetSitePromoQuery({ promoId: id });
|
||||
const [updateSitePromo] = useUpdateSitePromoMutation();
|
||||
const [deleteSitePromo] = useDeleteSitePromoMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
shortName: '',
|
||||
period: '',
|
||||
timer: '',
|
||||
timerBg: '',
|
||||
clinics: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
shortName: item.shortName ?? '',
|
||||
period: item.period ?? '',
|
||||
timer: item.timer ?? '',
|
||||
timerBg: item.timerBg ?? '',
|
||||
clinics: item.clinics == null ? '' : JSON.stringify(item.clinics, null, 2),
|
||||
linkServices: item.linkServices == null ? '' : JSON.stringify(item.linkServices, null, 2),
|
||||
linkStaff: item.linkStaff == null ? '' : JSON.stringify(item.linkStaff, null, 2),
|
||||
photos: item.photos == null ? '' : JSON.stringify(item.photos, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteSitePromo({ promoId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.shortName = form.shortName === '' ? null : form.shortName;
|
||||
data.period = form.period === '' ? null : form.period;
|
||||
data.timer = form.timer === '' ? null : form.timer;
|
||||
data.timerBg = form.timerBg === '' ? null : form.timerBg;
|
||||
data.clinics = !form.clinics || !String(form.clinics).trim() ? null : JSON.parse(form.clinics);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateSitePromo({ promoId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Промо с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: промо #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>Короткое название</label><input type="text" className="form-control" value={form.shortName} onChange={handleChange('shortName')} /></div>
|
||||
<div className="form-group"><label>Период</label><input type="text" className="form-control" value={form.period} onChange={handleChange('period')} /></div>
|
||||
<div className="form-group"><label>Таймер</label><input type="text" className="form-control" value={form.timer} onChange={handleChange('timer')} /></div>
|
||||
<div className="form-group"><label>Фон таймера</label><input type="text" className="form-control" value={form.timerBg} onChange={handleChange('timerBg')} /></div>
|
||||
<div className="form-group"><label>clinics (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.clinics} onChange={handleChange('clinics')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useGetSiteServicesQuery, useUpdateSiteServicesMutation, useDeleteSiteServicesMutation } from '/src/api/apiSiteServices';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { TextEditor } from '../components/Editors/TextEditor';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
import { NotFindElement } from '../components/Placeholders/NotFindElement';
|
||||
import { EditElementForm } from '../components/Forms/EditElementForm';
|
||||
import Modal from '../components/Modals/Modal';
|
||||
|
||||
export function EditSiteServicesPage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const navigateBack = () => navigate(`/site-services`);
|
||||
const regions = useSelector(selectRegions);
|
||||
const { data: item, isFetching, error } = useGetSiteServicesQuery({ siteServicesId: id });
|
||||
const [updateSiteServices] = useUpdateSiteServicesMutation();
|
||||
const [deleteSiteServices] = useDeleteSiteServicesMutation();
|
||||
const [isModalSuccess, setModalSuccess] = useState(false);
|
||||
const [errors, setErrors] = useState({ name: '', alias: '', regionId: '' });
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
active: false,
|
||||
regionId: '',
|
||||
alias: '',
|
||||
previewImg: '',
|
||||
partPrice: '',
|
||||
pokazaniya: '',
|
||||
preparation: '',
|
||||
protivopokazaniya: '',
|
||||
bannerImg: '',
|
||||
bannerImgM: '',
|
||||
bannerImgUrl: '',
|
||||
downloadFile: '',
|
||||
fullWidthBanner: '',
|
||||
kodUslug: '',
|
||||
linkPrice: '',
|
||||
photosTitle: '',
|
||||
contraindicationsList: '',
|
||||
customBlockText: '',
|
||||
customBlockText2: '',
|
||||
customBlockTitle: '',
|
||||
customBlockTitle2: '',
|
||||
indicationsList: '',
|
||||
plusList: '',
|
||||
plusText: '',
|
||||
plusTitle: '',
|
||||
prepareTitle: '',
|
||||
processText: '',
|
||||
processTitle: '',
|
||||
servicesList: '',
|
||||
servicesTitle: '',
|
||||
textUp: '',
|
||||
trainingText: '',
|
||||
whyText: '',
|
||||
whyTitle: '',
|
||||
hidePicture: '',
|
||||
linkVideoreviews: '',
|
||||
faq: '',
|
||||
hideSignBtn: '',
|
||||
quiz: '',
|
||||
tags: '',
|
||||
tagsImportant: '',
|
||||
clinics: '',
|
||||
staffUp: '',
|
||||
advantages: '',
|
||||
saleId: '',
|
||||
sortStaff: '',
|
||||
linkArticlesServices: '',
|
||||
servicesPhotos: '',
|
||||
linkFaq: '',
|
||||
linkServices: '',
|
||||
linkStaff: '',
|
||||
photos: '',
|
||||
});
|
||||
const [anons, setAnons] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return;
|
||||
setForm({
|
||||
name: item.name ?? '',
|
||||
active: Boolean(item.active),
|
||||
regionId: item.regionId ?? '',
|
||||
alias: item.alias ?? '',
|
||||
previewImg: item.previewImg ?? '',
|
||||
partPrice: item.partPrice ?? '',
|
||||
pokazaniya: item.pokazaniya ?? '',
|
||||
preparation: item.preparation ?? '',
|
||||
protivopokazaniya: item.protivopokazaniya ?? '',
|
||||
bannerImg: item.bannerImg ?? '',
|
||||
bannerImgM: item.bannerImgM ?? '',
|
||||
bannerImgUrl: item.bannerImgUrl ?? '',
|
||||
downloadFile: item.downloadFile ?? '',
|
||||
fullWidthBanner: item.fullWidthBanner ?? '',
|
||||
kodUslug: item.kodUslug ?? '',
|
||||
linkPrice: item.linkPrice ?? '',
|
||||
photosTitle: item.photosTitle ?? '',
|
||||
contraindicationsList: item.contraindicationsList ?? '',
|
||||
customBlockText: item.customBlockText ?? '',
|
||||
customBlockText2: item.customBlockText2 ?? '',
|
||||
customBlockTitle: item.customBlockTitle ?? '',
|
||||
customBlockTitle2: item.customBlockTitle2 ?? '',
|
||||
indicationsList: item.indicationsList ?? '',
|
||||
plusList: item.plusList ?? '',
|
||||
plusText: item.plusText ?? '',
|
||||
plusTitle: item.plusTitle ?? '',
|
||||
prepareTitle: item.prepareTitle ?? '',
|
||||
processText: item.processText ?? '',
|
||||
processTitle: item.processTitle ?? '',
|
||||
servicesList: item.servicesList ?? '',
|
||||
servicesTitle: item.servicesTitle ?? '',
|
||||
textUp: item.textUp ?? '',
|
||||
trainingText: item.trainingText ?? '',
|
||||
whyText: item.whyText ?? '',
|
||||
whyTitle: item.whyTitle ?? '',
|
||||
hidePicture: item.hidePicture ?? '',
|
||||
linkVideoreviews: item.linkVideoreviews == null ? '' : JSON.stringify(item.linkVideoreviews, null, 2),
|
||||
faq: item.faq == null ? '' : JSON.stringify(item.faq, null, 2),
|
||||
hideSignBtn: item.hideSignBtn == null ? '' : JSON.stringify(item.hideSignBtn, null, 2),
|
||||
quiz: item.quiz == null ? '' : JSON.stringify(item.quiz, null, 2),
|
||||
tags: item.tags == null ? '' : JSON.stringify(item.tags, null, 2),
|
||||
tagsImportant: item.tagsImportant == null ? '' : JSON.stringify(item.tagsImportant, null, 2),
|
||||
clinics: item.clinics == null ? '' : JSON.stringify(item.clinics, null, 2),
|
||||
staffUp: item.staffUp == null ? '' : JSON.stringify(item.staffUp, null, 2),
|
||||
advantages: item.advantages == null ? '' : JSON.stringify(item.advantages, null, 2),
|
||||
saleId: item.saleId == null ? '' : JSON.stringify(item.saleId, null, 2),
|
||||
sortStaff: item.sortStaff == null ? '' : JSON.stringify(item.sortStaff, null, 2),
|
||||
linkArticlesServices: item.linkArticlesServices == null ? '' : JSON.stringify(item.linkArticlesServices, null, 2),
|
||||
servicesPhotos: item.servicesPhotos == null ? '' : JSON.stringify(item.servicesPhotos, null, 2),
|
||||
linkFaq: item.linkFaq == null ? '' : JSON.stringify(item.linkFaq, null, 2),
|
||||
linkServices: item.linkServices == null ? '' : JSON.stringify(item.linkServices, null, 2),
|
||||
linkStaff: item.linkStaff == null ? '' : JSON.stringify(item.linkStaff, null, 2),
|
||||
photos: item.photos == null ? '' : JSON.stringify(item.photos, null, 2),
|
||||
});
|
||||
setAnons(item.anons ?? '');
|
||||
setContent(item.content ?? '');
|
||||
}, [item]);
|
||||
|
||||
const handleChange = (key) => (e) => {
|
||||
const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
|
||||
setForm((f) => ({ ...f, [key]: value }));
|
||||
if (key === 'name' || key === 'alias' || key === 'regionId') setErrors((err) => ({ ...err, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
try {
|
||||
await deleteSiteServices({ siteServicesId: id }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => navigateBack(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при удалении:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
const newErrors = { name: '', alias: '', regionId: '' };
|
||||
let hasError = false;
|
||||
if (!String(form.name ?? '').trim()) { newErrors.name = 'Название не может быть пустым'; hasError = true; }
|
||||
if (!String(form.alias ?? '').trim()) { newErrors.alias = 'Alias не может быть пустым'; hasError = true; }
|
||||
if (form.regionId === '' || form.regionId == null) { newErrors.regionId = 'Укажите регион'; hasError = true; }
|
||||
if (hasError) { setErrors(newErrors); window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
const data = { anons, content };
|
||||
try {
|
||||
data.name = form.name === '' ? null : form.name;
|
||||
data.active = Boolean(form.active);
|
||||
data.regionId = form.regionId === '' ? null : Number(form.regionId);
|
||||
data.alias = form.alias === '' ? null : form.alias;
|
||||
data.previewImg = form.previewImg === '' ? null : form.previewImg;
|
||||
data.partPrice = form.partPrice === '' ? null : form.partPrice;
|
||||
data.pokazaniya = form.pokazaniya === '' ? null : form.pokazaniya;
|
||||
data.preparation = form.preparation === '' ? null : form.preparation;
|
||||
data.protivopokazaniya = form.protivopokazaniya === '' ? null : form.protivopokazaniya;
|
||||
data.bannerImg = form.bannerImg === '' ? null : form.bannerImg;
|
||||
data.bannerImgM = form.bannerImgM === '' ? null : form.bannerImgM;
|
||||
data.bannerImgUrl = form.bannerImgUrl === '' ? null : form.bannerImgUrl;
|
||||
data.downloadFile = form.downloadFile === '' ? null : form.downloadFile;
|
||||
data.fullWidthBanner = form.fullWidthBanner === '' ? null : form.fullWidthBanner;
|
||||
data.kodUslug = form.kodUslug === '' ? null : form.kodUslug;
|
||||
data.linkPrice = form.linkPrice === '' ? null : form.linkPrice;
|
||||
data.photosTitle = form.photosTitle === '' ? null : form.photosTitle;
|
||||
data.contraindicationsList = form.contraindicationsList === '' ? null : form.contraindicationsList;
|
||||
data.customBlockText = form.customBlockText === '' ? null : form.customBlockText;
|
||||
data.customBlockText2 = form.customBlockText2 === '' ? null : form.customBlockText2;
|
||||
data.customBlockTitle = form.customBlockTitle === '' ? null : form.customBlockTitle;
|
||||
data.customBlockTitle2 = form.customBlockTitle2 === '' ? null : form.customBlockTitle2;
|
||||
data.indicationsList = form.indicationsList === '' ? null : form.indicationsList;
|
||||
data.plusList = form.plusList === '' ? null : form.plusList;
|
||||
data.plusText = form.plusText === '' ? null : form.plusText;
|
||||
data.plusTitle = form.plusTitle === '' ? null : form.plusTitle;
|
||||
data.prepareTitle = form.prepareTitle === '' ? null : form.prepareTitle;
|
||||
data.processText = form.processText === '' ? null : form.processText;
|
||||
data.processTitle = form.processTitle === '' ? null : form.processTitle;
|
||||
data.servicesList = form.servicesList === '' ? null : form.servicesList;
|
||||
data.servicesTitle = form.servicesTitle === '' ? null : form.servicesTitle;
|
||||
data.textUp = form.textUp === '' ? null : form.textUp;
|
||||
data.trainingText = form.trainingText === '' ? null : form.trainingText;
|
||||
data.whyText = form.whyText === '' ? null : form.whyText;
|
||||
data.whyTitle = form.whyTitle === '' ? null : form.whyTitle;
|
||||
data.hidePicture = form.hidePicture === '' ? null : Number(form.hidePicture);
|
||||
data.linkVideoreviews = !form.linkVideoreviews || !String(form.linkVideoreviews).trim() ? null : JSON.parse(form.linkVideoreviews);
|
||||
data.faq = !form.faq || !String(form.faq).trim() ? null : JSON.parse(form.faq);
|
||||
data.hideSignBtn = !form.hideSignBtn || !String(form.hideSignBtn).trim() ? null : JSON.parse(form.hideSignBtn);
|
||||
data.quiz = !form.quiz || !String(form.quiz).trim() ? null : JSON.parse(form.quiz);
|
||||
data.tags = !form.tags || !String(form.tags).trim() ? null : JSON.parse(form.tags);
|
||||
data.tagsImportant = !form.tagsImportant || !String(form.tagsImportant).trim() ? null : JSON.parse(form.tagsImportant);
|
||||
data.clinics = !form.clinics || !String(form.clinics).trim() ? null : JSON.parse(form.clinics);
|
||||
data.staffUp = !form.staffUp || !String(form.staffUp).trim() ? null : JSON.parse(form.staffUp);
|
||||
data.advantages = !form.advantages || !String(form.advantages).trim() ? null : JSON.parse(form.advantages);
|
||||
data.saleId = !form.saleId || !String(form.saleId).trim() ? null : JSON.parse(form.saleId);
|
||||
data.sortStaff = !form.sortStaff || !String(form.sortStaff).trim() ? null : JSON.parse(form.sortStaff);
|
||||
data.linkArticlesServices = !form.linkArticlesServices || !String(form.linkArticlesServices).trim() ? null : JSON.parse(form.linkArticlesServices);
|
||||
data.servicesPhotos = !form.servicesPhotos || !String(form.servicesPhotos).trim() ? null : JSON.parse(form.servicesPhotos);
|
||||
data.linkFaq = !form.linkFaq || !String(form.linkFaq).trim() ? null : JSON.parse(form.linkFaq);
|
||||
data.linkServices = !form.linkServices || !String(form.linkServices).trim() ? null : JSON.parse(form.linkServices);
|
||||
data.linkStaff = !form.linkStaff || !String(form.linkStaff).trim() ? null : JSON.parse(form.linkStaff);
|
||||
data.photos = !form.photos || !String(form.photos).trim() ? null : JSON.parse(form.photos);
|
||||
} catch (e) { window.alert('Пожалуйста исправьте ошибки в форме.'); return; }
|
||||
try {
|
||||
await updateSiteServices({ siteServicesId: id, data }).unwrap();
|
||||
setModalSuccess(true);
|
||||
window.setTimeout(() => window.location.reload(), 2000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка сохранения:', err);
|
||||
}
|
||||
};
|
||||
|
||||
if (isFetching) return <LoadingComponent />;
|
||||
if (error) return <ErrorComponent />;
|
||||
if (!item) return <NotFindElement message={`Услуга с ID=${id} не найдена.`} navigateBack={navigateBack} />;
|
||||
|
||||
return (
|
||||
<EditElementForm navigateBack={navigateBack} header={`Редактирование: услугу #${id}`} handleSave={handleSave} handleDelete={handleDelete}>
|
||||
<div className="form-group"><label>Название</label><input type="text" className="form-control" value={form.name} onChange={handleChange('name')} />
|
||||
{errors.name && <small className="text-danger">{errors.name}</small>}</div>
|
||||
<div className="form-group form-check"><input type="checkbox" className="form-check-input" id="active" checked={Boolean(form.active)} onChange={handleChange('active')} /><label className="form-check-label" htmlFor="active">Активно</label></div>
|
||||
<div className="form-group"><label>Регион</label><select className="form-control" value={form.regionId === '' || form.regionId == null ? '' : String(form.regionId)} onChange={handleChange('regionId')}><option value="">—</option>{Object.entries(regions).map(([rid, name]) => (<option key={rid} value={rid}>{name}</option>))}</select>
|
||||
{errors.regionId && <small className="text-danger">{errors.regionId}</small>}</div>
|
||||
<div className="form-group"><label>Alias</label><input type="text" className="form-control" value={form.alias} onChange={handleChange('alias')} />
|
||||
{errors.alias && <small className="text-danger">{errors.alias}</small>}</div>
|
||||
<div className="form-group"><label>Анонс</label><TextEditor content={anons} setContent={setAnons} /></div>
|
||||
<div className="form-group"><label>Контент</label><TextEditor content={content} setContent={setContent} /></div>
|
||||
<div className="form-group"><label>previewImg</label><input type="text" className="form-control" value={form.previewImg} onChange={handleChange('previewImg')} /></div>
|
||||
<div className="form-group"><label>partPrice</label><input type="text" className="form-control" value={form.partPrice} onChange={handleChange('partPrice')} /></div>
|
||||
<div className="form-group"><label>pokazaniya</label><input type="text" className="form-control" value={form.pokazaniya} onChange={handleChange('pokazaniya')} /></div>
|
||||
<div className="form-group"><label>preparation</label><input type="text" className="form-control" value={form.preparation} onChange={handleChange('preparation')} /></div>
|
||||
<div className="form-group"><label>protivopokazaniya</label><input type="text" className="form-control" value={form.protivopokazaniya} onChange={handleChange('protivopokazaniya')} /></div>
|
||||
<div className="form-group"><label>bannerImg</label><input type="text" className="form-control" value={form.bannerImg} onChange={handleChange('bannerImg')} /></div>
|
||||
<div className="form-group"><label>bannerImgM</label><input type="text" className="form-control" value={form.bannerImgM} onChange={handleChange('bannerImgM')} /></div>
|
||||
<div className="form-group"><label>bannerImgUrl</label><input type="text" className="form-control" value={form.bannerImgUrl} onChange={handleChange('bannerImgUrl')} /></div>
|
||||
<div className="form-group"><label>downloadFile</label><input type="text" className="form-control" value={form.downloadFile} onChange={handleChange('downloadFile')} /></div>
|
||||
<div className="form-group"><label>fullWidthBanner</label><input type="text" className="form-control" value={form.fullWidthBanner} onChange={handleChange('fullWidthBanner')} /></div>
|
||||
<div className="form-group"><label>kodUslug</label><input type="text" className="form-control" value={form.kodUslug} onChange={handleChange('kodUslug')} /></div>
|
||||
<div className="form-group"><label>linkPrice</label><input type="text" className="form-control" value={form.linkPrice} onChange={handleChange('linkPrice')} /></div>
|
||||
<div className="form-group"><label>photosTitle</label><input type="text" className="form-control" value={form.photosTitle} onChange={handleChange('photosTitle')} /></div>
|
||||
<div className="form-group"><label>contraindicationsList</label><input type="text" className="form-control" value={form.contraindicationsList} onChange={handleChange('contraindicationsList')} /></div>
|
||||
<div className="form-group"><label>customBlockText</label><input type="text" className="form-control" value={form.customBlockText} onChange={handleChange('customBlockText')} /></div>
|
||||
<div className="form-group"><label>customBlockText2</label><input type="text" className="form-control" value={form.customBlockText2} onChange={handleChange('customBlockText2')} /></div>
|
||||
<div className="form-group"><label>customBlockTitle</label><input type="text" className="form-control" value={form.customBlockTitle} onChange={handleChange('customBlockTitle')} /></div>
|
||||
<div className="form-group"><label>customBlockTitle2</label><input type="text" className="form-control" value={form.customBlockTitle2} onChange={handleChange('customBlockTitle2')} /></div>
|
||||
<div className="form-group"><label>indicationsList</label><input type="text" className="form-control" value={form.indicationsList} onChange={handleChange('indicationsList')} /></div>
|
||||
<div className="form-group"><label>plusList</label><input type="text" className="form-control" value={form.plusList} onChange={handleChange('plusList')} /></div>
|
||||
<div className="form-group"><label>plusText</label><input type="text" className="form-control" value={form.plusText} onChange={handleChange('plusText')} /></div>
|
||||
<div className="form-group"><label>plusTitle</label><input type="text" className="form-control" value={form.plusTitle} onChange={handleChange('plusTitle')} /></div>
|
||||
<div className="form-group"><label>prepareTitle</label><input type="text" className="form-control" value={form.prepareTitle} onChange={handleChange('prepareTitle')} /></div>
|
||||
<div className="form-group"><label>processText</label><input type="text" className="form-control" value={form.processText} onChange={handleChange('processText')} /></div>
|
||||
<div className="form-group"><label>processTitle</label><input type="text" className="form-control" value={form.processTitle} onChange={handleChange('processTitle')} /></div>
|
||||
<div className="form-group"><label>servicesList</label><input type="text" className="form-control" value={form.servicesList} onChange={handleChange('servicesList')} /></div>
|
||||
<div className="form-group"><label>servicesTitle</label><input type="text" className="form-control" value={form.servicesTitle} onChange={handleChange('servicesTitle')} /></div>
|
||||
<div className="form-group"><label>textUp</label><input type="text" className="form-control" value={form.textUp} onChange={handleChange('textUp')} /></div>
|
||||
<div className="form-group"><label>trainingText</label><input type="text" className="form-control" value={form.trainingText} onChange={handleChange('trainingText')} /></div>
|
||||
<div className="form-group"><label>whyText</label><input type="text" className="form-control" value={form.whyText} onChange={handleChange('whyText')} /></div>
|
||||
<div className="form-group"><label>whyTitle</label><input type="text" className="form-control" value={form.whyTitle} onChange={handleChange('whyTitle')} /></div>
|
||||
<div className="form-group"><label>hidePicture</label><input type="number" className="form-control" value={form.hidePicture} onChange={handleChange('hidePicture')} /></div>
|
||||
<div className="form-group"><label>linkVideoreviews (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkVideoreviews} onChange={handleChange('linkVideoreviews')} /></div>
|
||||
<div className="form-group"><label>faq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.faq} onChange={handleChange('faq')} /></div>
|
||||
<div className="form-group"><label>hideSignBtn (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.hideSignBtn} onChange={handleChange('hideSignBtn')} /></div>
|
||||
<div className="form-group"><label>quiz (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.quiz} onChange={handleChange('quiz')} /></div>
|
||||
<div className="form-group"><label>tags (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tags} onChange={handleChange('tags')} /></div>
|
||||
<div className="form-group"><label>tagsImportant (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.tagsImportant} onChange={handleChange('tagsImportant')} /></div>
|
||||
<div className="form-group"><label>clinics (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.clinics} onChange={handleChange('clinics')} /></div>
|
||||
<div className="form-group"><label>staffUp (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.staffUp} onChange={handleChange('staffUp')} /></div>
|
||||
<div className="form-group"><label>advantages (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.advantages} onChange={handleChange('advantages')} /></div>
|
||||
<div className="form-group"><label>saleId (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.saleId} onChange={handleChange('saleId')} /></div>
|
||||
<div className="form-group"><label>sortStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.sortStaff} onChange={handleChange('sortStaff')} /></div>
|
||||
<div className="form-group"><label>linkArticlesServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkArticlesServices} onChange={handleChange('linkArticlesServices')} /></div>
|
||||
<div className="form-group"><label>servicesPhotos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.servicesPhotos} onChange={handleChange('servicesPhotos')} /></div>
|
||||
<div className="form-group"><label>linkFaq (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkFaq} onChange={handleChange('linkFaq')} /></div>
|
||||
<div className="form-group"><label>linkServices (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkServices} onChange={handleChange('linkServices')} /></div>
|
||||
<div className="form-group"><label>linkStaff (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.linkStaff} onChange={handleChange('linkStaff')} /></div>
|
||||
<div className="form-group"><label>photos (JSON)</label><textarea className="form-control font-monospace" rows={4} value={form.photos} onChange={handleChange('photos')} /></div>
|
||||
<Modal isOpen={isModalSuccess} title="Изменения внесены" hasButtons={false}><p className="mb-1">Изменения успешно внесены.</p></Modal>
|
||||
</EditElementForm>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetMedicalCenterListQuery } from '/src/api/apiMedicalCenter';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const MedicalCenterListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetMedicalCenterListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination || {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Медцентры</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/medical-center/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 4 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/medical-center/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetNewsListQuery } from '/src/api/apiNews';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const NewsListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetNewsListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination || {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Новости</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/news/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
<th>Регион</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
<td>{ regions[item.regionId] ?? item.regionId }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 5 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/news/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetSitePromoListQuery } from '/src/api/apiSitePromo';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const SitePromoListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetSitePromoListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination || {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Промо (контент)</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/site-promo/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
<th>Регион</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
<td>{ regions[item.regionId] ?? item.regionId }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 5 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/site-promo/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { useGetSiteServicesListQuery } from '/src/api/apiSiteServices';
|
||||
import { selectRegions } from '../store/slice/regionSlice';
|
||||
import { useOutsideClick } from '../hooks/useOutsideClick';
|
||||
import { LoadingComponent } from '../components/Placeholders/LoadingComponent';
|
||||
import { ErrorComponent } from '../components/Placeholders/ErrorComponent';
|
||||
|
||||
export const SiteServicesListPage = () => {
|
||||
const [ searchValue, setSearchValue ] = useState( '' );
|
||||
const [ currentPage, setCurrentPage ] = useState( 1 );
|
||||
const [ expandedId, setExpandedId ] = useState( '' );
|
||||
const navigate = useNavigate();
|
||||
const regions = useSelector(selectRegions);
|
||||
const tableRef = useRef( null );
|
||||
useOutsideClick( tableRef, () => setExpandedId( null ));
|
||||
|
||||
const { data: response = {}, isFetching, error: queryError } =
|
||||
useGetSiteServicesListQuery( { search: searchValue, page: currentPage } );
|
||||
const pagination = response.pagination || {};
|
||||
const items = response.data ? response.data : [];
|
||||
|
||||
const renderPagination = () => {
|
||||
const total = pagination.total_pages || 1;
|
||||
const current = pagination.current_page || 1;
|
||||
const pages = new Set( [ 1, total ] );
|
||||
for ( let page = current - 2; page <= current + 2; page += 1 ) {
|
||||
if ( page > 1 && page < total ) pages.add( page );
|
||||
}
|
||||
const sorted = Array.from( pages ).sort( ( a, b ) => a - b );
|
||||
const elements = [];
|
||||
let last = 0;
|
||||
sorted.forEach( page => {
|
||||
if ( last && page - last > 1 ) {
|
||||
elements.push(
|
||||
<li key={ `dots-${last}` } className="page-item disabled">
|
||||
<span className="page-link">…</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
elements.push(
|
||||
<li key={ page } className={`page-item ${ page === current ? 'active' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => page !== current && setCurrentPage( page ) }>
|
||||
{ page }
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
last = page;
|
||||
});
|
||||
return (
|
||||
<nav>
|
||||
<ul className="pagination justify-content-center">
|
||||
<li className={`page-item ${ !pagination.has_previous_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_previous_page && setCurrentPage(current - 1) }>«</button>
|
||||
</li>
|
||||
{ elements }
|
||||
<li className={`page-item ${ !pagination.has_next_page ? 'disabled' : '' }`}>
|
||||
<button type="button" className="page-link" onClick={ () => pagination.has_next_page && setCurrentPage(current + 1) }>»</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<h1 className="h3 mb-4 text-gray-800">Услуги сайта</h1>
|
||||
<div className="d-flex justify-content-between mb-3" style={{ marginRight: '0.1rem', marginLeft: '0.1rem' }}>
|
||||
<div className="form-group align-self-end mr-3">
|
||||
<input type="button" className="btn btn-outline-primary" value="Добавить" onClick={ e => { e.stopPropagation(); navigate(`/site-services/create`); } } />
|
||||
</div>
|
||||
<div className="form-group flex-grow-1">
|
||||
<label>Поиск</label>
|
||||
<input type="text" className="form-control" value={ searchValue } onChange={ e => { setSearchValue( e.target.value ); setCurrentPage( 1 ); } } />
|
||||
</div>
|
||||
</div>
|
||||
{ isFetching ? <LoadingComponent /> : queryError ? <ErrorComponent /> : (
|
||||
<>
|
||||
<div className="table-responsive" ref={tableRef}>
|
||||
<table className="table table-hover table-bordered">
|
||||
<thead><tr>
|
||||
<th>ID</th>
|
||||
<th>Название</th>
|
||||
<th>Alias</th>
|
||||
<th>Активно</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{items.map( item => (
|
||||
<>
|
||||
<tr key={ item.id } className={ `cursor-pointer${ expandedId === item.id ? ' table-success' : '' }` } onClick={ () => setExpandedId( expandedId === item.id ? null : item.id ) }>
|
||||
<td>{ item.id }</td>
|
||||
<td>{ item.name }</td>
|
||||
<td>{ item.alias }</td>
|
||||
<td>{ item.active ? 'Да' : 'Нет' }</td>
|
||||
</tr>
|
||||
{ expandedId === item.id && (
|
||||
<tr className='table-success'>
|
||||
<td colSpan={ 4 }>
|
||||
<input type="button" className="btn btn-outline-primary" value="Редактировать" onClick={ e => { e.stopPropagation(); navigate(`/site-services/edit/${item.id}`) } } />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{ renderPagination() }
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,12 @@ import { API } from '../api/apiSlice';
|
||||
import '../api/apiDepartment'
|
||||
import '../api/apiFilial'
|
||||
import '../api/apiSpecialist'
|
||||
import '../api/apiNews'
|
||||
import '../api/apiSitePromo'
|
||||
import '../api/apiDisease'
|
||||
import '../api/apiMedicalCenter'
|
||||
import '../api/apiArticle'
|
||||
import '../api/apiSiteServices'
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
|
||||
Reference in New Issue
Block a user