fixed global styles issue

This commit is contained in:
Dmitri 2026-03-31 10:37:41 +04:00
parent e6b4fe69c7
commit a31af13c84
3 changed files with 219 additions and 147 deletions

View File

@ -0,0 +1,80 @@
'use strict';
/**
* Remove invalid element_type_defaults entries.
* Only valid element types defined in DEFAULT_ROWS should exist.
*/
module.exports = {
async up(queryInterface, Sequelize) {
// Valid element types as defined in element_type_defaults.js DEFAULT_ROWS
const validTypes = [
'navigation_next',
'navigation_prev',
'tooltip',
'description',
'gallery',
'carousel',
'video_player',
'audio_player',
'spot',
'logo',
'popup',
];
// Find invalid entries
const invalidEntries = await queryInterface.sequelize.query(
`SELECT id, element_type, name
FROM element_type_defaults
WHERE element_type NOT IN (:validTypes)
AND "deletedAt" IS NULL`,
{
replacements: { validTypes },
type: Sequelize.QueryTypes.SELECT,
},
);
if (invalidEntries.length === 0) {
console.log('No invalid element_type_defaults found.');
return;
}
console.log(
`Found ${invalidEntries.length} invalid element_type_defaults:`,
);
invalidEntries.forEach((entry) => {
console.log(` - ${entry.name} (${entry.element_type})`);
});
// Delete invalid entries
const idsToDelete = invalidEntries.map((e) => e.id);
await queryInterface.sequelize.query(
`DELETE FROM element_type_defaults WHERE id IN (:ids)`,
{ replacements: { ids: idsToDelete } },
);
// Also delete from project_element_defaults
const deletedProjectDefaults = await queryInterface.sequelize.query(
`DELETE FROM project_element_defaults
WHERE element_type NOT IN (:validTypes)
RETURNING id, element_type`,
{
replacements: { validTypes },
type: Sequelize.QueryTypes.SELECT,
},
);
console.log(
`Deleted ${idsToDelete.length} invalid element_type_defaults.`,
);
console.log(
`Deleted ${deletedProjectDefaults.length} invalid project_element_defaults.`,
);
},
async down(_queryInterface, _Sequelize) {
// Cannot restore deleted invalid entries
console.log(
'Down migration not applicable - invalid entries cannot be restored.',
);
},
};

View File

@ -106,76 +106,70 @@ const CarouselSettingsSection: React.FC<CarouselSettingsSectionProps> = ({
</FormField>
</div>
<div className='mb-3 mt-4 flex items-center justify-between'>
<h3 className='text-sm font-semibold'>Carousel slides</h3>
<BaseButton
color='info'
icon={mdiPlus}
small
label='Add slide'
onClick={onAddSlide}
/>
</div>
{/* Slides editor only shown in constructor - slides are instance-specific */}
{isConstructor && (
<>
<div className='mb-3 mt-4 flex items-center justify-between'>
<h3 className='text-sm font-semibold'>Carousel slides</h3>
<BaseButton
color='info'
icon={mdiPlus}
small
label='Add slide'
onClick={onAddSlide}
/>
</div>
<div className='space-y-4'>
{carouselSlides.length === 0 ? (
<p className='text-sm text-gray-500'>No slides yet.</p>
) : (
carouselSlides.map((slide, index) => (
<CardBox
key={slide.id}
className='border border-gray-200 dark:border-dark-700'
>
<div className='mb-3 flex items-center justify-between'>
<h4 className='text-sm font-semibold'>Slide {index + 1}</h4>
<BaseButton
color='danger'
icon={mdiTrashCan}
small
outline
onClick={() => onRemoveSlide(slide.id)}
/>
</div>
<div className='grid gap-3 md:grid-cols-2'>
{isConstructor ? (
<FormField label='Image'>
<select
value={slide.imageUrl}
onChange={(event) =>
onUpdateSlide(slide.id, 'imageUrl', event.target.value)
}
>
<option value=''>Not selected</option>
{imageAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</FormField>
) : (
<FormField label='Image URL'>
<input
value={slide.imageUrl}
onChange={(event) =>
onUpdateSlide(slide.id, 'imageUrl', event.target.value)
}
<div className='space-y-4'>
{carouselSlides.length === 0 ? (
<p className='text-sm text-gray-500'>No slides yet.</p>
) : (
carouselSlides.map((slide, index) => (
<CardBox
key={slide.id}
className='border border-gray-200 dark:border-dark-700'
>
<div className='mb-3 flex items-center justify-between'>
<h4 className='text-sm font-semibold'>Slide {index + 1}</h4>
<BaseButton
color='danger'
icon={mdiTrashCan}
small
outline
onClick={() => onRemoveSlide(slide.id)}
/>
</FormField>
)}
<FormField label='Caption'>
<input
value={slide.caption}
onChange={(event) =>
onUpdateSlide(slide.id, 'caption', event.target.value)
}
/>
</FormField>
</div>
</CardBox>
))
)}
</div>
</div>
<div className='grid gap-3 md:grid-cols-2'>
<FormField label='Image'>
<select
value={slide.imageUrl}
onChange={(event) =>
onUpdateSlide(slide.id, 'imageUrl', event.target.value)
}
>
<option value=''>Not selected</option>
{imageAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</FormField>
<FormField label='Caption'>
<input
value={slide.caption}
onChange={(event) =>
onUpdateSlide(slide.id, 'caption', event.target.value)
}
/>
</FormField>
</div>
</CardBox>
))
)}
</div>
</>
)}
</CardBox>
);
};

View File

@ -63,87 +63,85 @@ const GallerySettingsSection: React.FC<GallerySettingsSectionProps> = ({
</FormField>
</div>
<div className='mb-3 flex items-center justify-between'>
<h3 className='text-sm font-semibold'>Gallery cards</h3>
<BaseButton
color='info'
icon={mdiPlus}
small
label='Add card'
onClick={onAddCard}
/>
</div>
{/* Cards editor only shown in constructor - cards are instance-specific */}
{isConstructor && (
<>
<div className='mb-3 flex items-center justify-between'>
<h3 className='text-sm font-semibold'>Gallery cards</h3>
<BaseButton
color='info'
icon={mdiPlus}
small
label='Add card'
onClick={onAddCard}
/>
</div>
<div className='space-y-4'>
{galleryCards.length === 0 ? (
<p className='text-sm text-gray-500'>No cards yet.</p>
) : (
galleryCards.map((card, index) => (
<CardBox
key={card.id}
className='border border-gray-200 dark:border-dark-700'
>
<div className='mb-3 flex items-center justify-between'>
<h4 className='text-sm font-semibold'>Card {index + 1}</h4>
<BaseButton
color='danger'
icon={mdiTrashCan}
small
outline
onClick={() => onRemoveCard(card.id)}
/>
</div>
<div className='grid gap-3 md:grid-cols-2'>
{isConstructor ? (
<FormField label='Image'>
<select
value={card.imageUrl}
onChange={(event) =>
onUpdateCard(card.id, 'imageUrl', event.target.value)
}
>
<option value=''>Not selected</option>
{imageAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</FormField>
) : (
<FormField label='Image URL'>
<input
value={card.imageUrl}
onChange={(event) =>
onUpdateCard(card.id, 'imageUrl', event.target.value)
}
<div className='space-y-4'>
{galleryCards.length === 0 ? (
<p className='text-sm text-gray-500'>No cards yet.</p>
) : (
galleryCards.map((card, index) => (
<CardBox
key={card.id}
className='border border-gray-200 dark:border-dark-700'
>
<div className='mb-3 flex items-center justify-between'>
<h4 className='text-sm font-semibold'>Card {index + 1}</h4>
<BaseButton
color='danger'
icon={mdiTrashCan}
small
outline
onClick={() => onRemoveCard(card.id)}
/>
</FormField>
)}
<FormField label='Title'>
<input
value={card.title}
onChange={(event) =>
onUpdateCard(card.id, 'title', event.target.value)
}
/>
</FormField>
<div className='md:col-span-2'>
<FormField label='Description' hasTextareaHeight>
<textarea
value={card.description}
onChange={(event) =>
onUpdateCard(card.id, 'description', event.target.value)
}
className='h-24 w-full rounded border border-gray-300 p-2 dark:border-dark-700 dark:bg-dark-900'
/>
</FormField>
</div>
</div>
</CardBox>
))
)}
</div>
</div>
<div className='grid gap-3 md:grid-cols-2'>
<FormField label='Image'>
<select
value={card.imageUrl}
onChange={(event) =>
onUpdateCard(card.id, 'imageUrl', event.target.value)
}
>
<option value=''>Not selected</option>
{imageAssetOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</FormField>
<FormField label='Title'>
<input
value={card.title}
onChange={(event) =>
onUpdateCard(card.id, 'title', event.target.value)
}
/>
</FormField>
<div className='md:col-span-2'>
<FormField label='Description' hasTextareaHeight>
<textarea
value={card.description}
onChange={(event) =>
onUpdateCard(
card.id,
'description',
event.target.value,
)
}
className='h-24 w-full rounded border border-gray-300 p-2 dark:border-dark-700 dark:bg-dark-900'
/>
</FormField>
</div>
</div>
</CardBox>
))
)}
</div>
</>
)}
</CardBox>
);
};