feat: Add Global Module Settings and Caption to Images

- Introduced GlobalModuleSettings model for platform-wide module enable/disable settings.
- Added 'caption' field to Images model to store image captions.
- Updated GenerateImagePromptsFunction to handle new caption structure in prompts.
- Enhanced AIPromptViewSet to return global prompt types and validate active prompts.
- Modified serializers and views to accommodate new caption field and global settings.
- Updated frontend components to display captions and filter prompts based on active types.
- Created migrations for GlobalModuleSettings and added caption field to Images.
This commit is contained in:
IGNY8 VPS (Salman)
2025-12-20 21:34:59 +00:00
parent 9e8ff4fbb1
commit 7a1e952a57
16 changed files with 370 additions and 383 deletions

View File

@@ -73,6 +73,7 @@ export default function Prompts() {
const [prompts, setPrompts] = useState<Record<string, PromptData>>({});
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState<Record<string, boolean>>({});
const [activePromptTypes, setActivePromptTypes] = useState<string[]>([]);
// Load all prompts
useEffect(() => {
@@ -82,7 +83,15 @@ export default function Prompts() {
const loadPrompts = async () => {
setLoading(true);
try {
const promises = PROMPT_TYPES.map(async (type) => {
// First, get the list of globally active prompt types
const activeTypesResponse = await fetchAPI('/v1/system/prompts/active_types/');
const activeTypes = activeTypesResponse.active_types || [];
setActivePromptTypes(activeTypes);
// Only load prompts that are globally active
const activePromptConfigs = PROMPT_TYPES.filter(type => activeTypes.includes(type.key));
const promises = activePromptConfigs.map(async (type) => {
try {
// fetchAPI extracts data from unified format {success: true, data: {...}}
// So response IS the data object
@@ -100,15 +109,6 @@ export default function Prompts() {
results.forEach(({ key, data }) => {
if (data) {
promptsMap[key] = data;
} else {
// Use default if not found
promptsMap[key] = {
prompt_type: key,
prompt_type_display: PROMPT_TYPES.find(t => t.key === key)?.label || key,
prompt_value: '',
default_prompt: '',
is_active: true,
};
}
});
@@ -218,6 +218,7 @@ export default function Prompts() {
<div className="p-6">
{/* Planner Prompts Section */}
{PROMPT_TYPES.filter(t => ['clustering', 'ideas'].includes(t.key) && activePromptTypes.includes(t.key)).length > 0 && (
<div className="mb-8">
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-1">
@@ -231,7 +232,7 @@ export default function Prompts() {
{/* 2-Column Grid for Planner Prompts */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Clustering Prompt */}
{PROMPT_TYPES.filter(t => ['clustering', 'ideas'].includes(t.key)).map((type) => {
{PROMPT_TYPES.filter(t => ['clustering', 'ideas'].includes(t.key) && activePromptTypes.includes(t.key)).map((type) => {
const prompt = prompts[type.key] || {
prompt_type: type.key,
prompt_type_display: type.label,
@@ -287,8 +288,10 @@ export default function Prompts() {
})}
</div>
</div>
)}
{/* Writer Prompts Section */}
{PROMPT_TYPES.filter(t => t.key === 'content_generation' && activePromptTypes.includes(t.key)).length > 0 && (
<div className="mb-8">
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-1">
@@ -301,7 +304,7 @@ export default function Prompts() {
{/* Content Generation Prompt */}
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
{PROMPT_TYPES.filter(t => t.key === 'content_generation').map((type) => {
{PROMPT_TYPES.filter(t => t.key === 'content_generation' && activePromptTypes.includes(t.key)).map((type) => {
const prompt = prompts[type.key] || {
prompt_type: type.key,
prompt_type_display: type.label,
@@ -357,8 +360,10 @@ export default function Prompts() {
})}
</div>
</div>
)}
{/* Image Generation Section */}
{PROMPT_TYPES.filter(t => ['image_prompt_extraction', 'image_prompt_template', 'negative_prompt'].includes(t.key) && activePromptTypes.includes(t.key)).length > 0 && (
<div className="mb-8">
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-1">
@@ -371,7 +376,7 @@ export default function Prompts() {
{/* 2-Column Grid for Image Prompts */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{PROMPT_TYPES.filter(t => ['image_prompt_extraction', 'image_prompt_template', 'negative_prompt'].includes(t.key)).map((type) => {
{PROMPT_TYPES.filter(t => ['image_prompt_extraction', 'image_prompt_template', 'negative_prompt'].includes(t.key) && activePromptTypes.includes(t.key)).map((type) => {
const prompt = prompts[type.key] || {
prompt_type: type.key,
prompt_type_display: type.label,
@@ -429,8 +434,10 @@ export default function Prompts() {
})}
</div>
</div>
)}
{/* Site Builder Prompts Section */}
{PROMPT_TYPES.filter(t => t.key === 'site_structure_generation' && activePromptTypes.includes(t.key)).length > 0 && (
<div className="mb-8">
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 dark:text-white mb-1">
@@ -443,7 +450,7 @@ export default function Prompts() {
{/* Site Structure Generation Prompt */}
<div className="rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900">
{PROMPT_TYPES.filter(t => t.key === 'site_structure_generation').map((type) => {
{PROMPT_TYPES.filter(t => t.key === 'site_structure_generation' && activePromptTypes.includes(t.key)).map((type) => {
const prompt = prompts[type.key] || {
prompt_type: type.key,
prompt_type_display: type.label,
@@ -508,6 +515,7 @@ export default function Prompts() {
})}
</div>
</div>
)}
</div>
</>
);