Skip to content

Commit dc294bd

Browse files
committed
Add theme utilities, upgrade prompts, and enhance license features
1 parent b806d55 commit dc294bd

30 files changed

Lines changed: 3990 additions & 547 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ Language automatically follows your Strapi admin interface setting.
644644
645645
## 🙏 Acknowledgments
646646
647-
Built with ❤️ for the Strapi community.
647+
Built for the Strapi community.
648648
649649
Special thanks to:
650650
- Strapi team for the amazing CMS

admin/src/components/AdvancedFilterButton.tsx

Lines changed: 90 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-nocheck
22
import React, { useState, useEffect } from 'react';
33
import { Button } from '@strapi/design-system';
4-
import { Filter } from '@strapi/icons';
4+
import { Filter, Check } from '@strapi/icons';
55
import { useNavigate, useLocation } from 'react-router-dom';
66
import { useFetchClient } from '@strapi/strapi/admin';
77
import SimpleAdvancedFilterModal from './SimpleAdvancedFilterModal';
@@ -12,7 +12,7 @@ const AdvancedFilterButton: React.FC = () => {
1212
const location = useLocation();
1313
const { get } = useFetchClient();
1414
const [availableFields, setAvailableFields] = useState<Array<{ name: string; type: string }>>([]);
15-
const [availableRelations, setAvailableRelations] = useState<Array<{ name: string }>>([]);
15+
const [availableRelations, setAvailableRelations] = useState<Array<{ name: string; target: string }>>([]);
1616
const [hasActiveFilters, setHasActiveFilters] = useState(false);
1717

1818
// Check if URL has filters
@@ -34,7 +34,9 @@ const AdvancedFilterButton: React.FC = () => {
3434
return match ? match[1] : null;
3535
};
3636

37-
// Fetch schema for current content type
37+
/**
38+
* Fetches schema for current content type and extracts fields + relations
39+
*/
3840
useEffect(() => {
3941
const fetchSchema = async () => {
4042
const uid = extractContentTypeUid();
@@ -46,106 +48,85 @@ const AdvancedFilterButton: React.FC = () => {
4648
try {
4749
console.log('[AdvancedFilter] Fetching schema for:', uid);
4850

49-
// Try to get the actual content type schema
50-
let schemaData;
51+
// Step 1: Get the REAL schema from content-type-builder (has correct attribute types)
52+
let realAttributes: Record<string, any> = {};
5153
try {
52-
const response = await get(`/content-manager/content-types/${uid}/configuration`);
53-
schemaData = response.data;
54-
console.log('[AdvancedFilter] Configuration response:', schemaData);
54+
const schemaResponse = await get(`/content-type-builder/content-types/${uid}`);
55+
const schemaInfo = schemaResponse.data?.data?.schema || schemaResponse.data?.schema || {};
56+
realAttributes = schemaInfo.attributes || {};
57+
console.log('[AdvancedFilter] Real schema attributes:', realAttributes);
5558
} catch (e) {
56-
console.log('[AdvancedFilter] Configuration endpoint failed, trying direct strapi schema');
59+
console.log('[AdvancedFilter] Content-type-builder endpoint failed:', e);
5760
}
5861

59-
// Extract attributes from the layout/metadatas
60-
// The structure is: response.data.data.contentType
61-
const contentTypeData = schemaData?.data?.contentType || schemaData?.contentType || {};
62-
const metadatas = contentTypeData.metadatas || {};
63-
const layouts = contentTypeData.layouts || {};
64-
65-
console.log('[AdvancedFilter] Metadatas:', metadatas);
66-
console.log('[AdvancedFilter] Layouts:', layouts);
67-
68-
// Get fields from edit layout
69-
let allFieldNames = new Set<string>();
70-
71-
// From metadatas (most reliable)
72-
Object.keys(metadatas).forEach(key => allFieldNames.add(key));
73-
74-
// From edit layout
75-
if (layouts.edit) {
76-
layouts.edit.forEach((row: any) => {
77-
row.forEach((field: any) => {
78-
if (field.name) allFieldNames.add(field.name);
79-
});
80-
});
62+
// Step 2: Also get configuration for field metadata (labels, etc.)
63+
let metadatas: Record<string, any> = {};
64+
try {
65+
const configResponse = await get(`/content-manager/content-types/${uid}/configuration`);
66+
const contentTypeData = configResponse.data?.data?.contentType || configResponse.data?.contentType || {};
67+
metadatas = contentTypeData.metadatas || {};
68+
console.log('[AdvancedFilter] Configuration metadatas:', metadatas);
69+
} catch (e) {
70+
console.log('[AdvancedFilter] Configuration endpoint failed:', e);
8171
}
8272

83-
// From list layout
84-
if (layouts.list) {
85-
layouts.list.forEach((fieldName: string) => allFieldNames.add(fieldName));
86-
}
73+
// Step 3: Extract fields and relations from REAL attributes
74+
const fields: Array<{ name: string; type: string }> = [];
75+
const relations: Array<{ name: string; target: string }> = [];
76+
const fieldNames = new Set<string>();
8777

88-
const attributes = {};
89-
allFieldNames.forEach(name => {
90-
const metadata = metadatas[name];
91-
attributes[name] = {
92-
type: metadata?.edit?.type || 'string',
93-
};
78+
// Process real attributes (this has correct types!)
79+
Object.keys(realAttributes).forEach((key) => {
80+
const attr = realAttributes[key];
81+
const attrType = attr.type;
82+
83+
// Skip internal fields
84+
if (['createdBy', 'updatedBy', 'localizations', 'locale'].includes(key)) {
85+
return;
86+
}
87+
88+
// Check if it's a relation
89+
if (attrType === 'relation') {
90+
relations.push({ name: key, target: attr.target });
91+
console.log('[AdvancedFilter] Found relation:', key, '-> target:', attr.target);
92+
} else if (attrType === 'component' || attrType === 'dynamiczone') {
93+
// Skip components and dynamic zones for filtering
94+
console.log('[AdvancedFilter] Skipping component/dynamiczone:', key);
95+
} else if (attrType === 'media') {
96+
// Skip media for now (complex filtering)
97+
console.log('[AdvancedFilter] Skipping media:', key);
98+
} else if (!fieldNames.has(key)) {
99+
// Regular field
100+
fieldNames.add(key);
101+
fields.push({
102+
name: key,
103+
type: attrType || 'string'
104+
});
105+
}
94106
});
107+
108+
// Add default fields if not already present
109+
['id', 'documentId', 'createdAt', 'updatedAt'].forEach(defaultField => {
110+
if (!fieldNames.has(defaultField)) {
111+
let type = 'string';
112+
if (defaultField === 'id') type = 'integer';
113+
if (defaultField === 'createdAt' || defaultField === 'updatedAt') type = 'datetime';
114+
fields.unshift({ name: defaultField, type });
115+
}
116+
});
117+
118+
setAvailableFields(fields);
119+
setAvailableRelations(relations);
95120

96-
console.log('[AdvancedFilter] Reconstructed attributes:', attributes);
121+
console.log('[AdvancedFilter] Final fields:', fields);
122+
console.log('[AdvancedFilter] Final relations:', relations);
97123

98-
if (attributes && Object.keys(attributes).length > 0) {
99-
// Extract filterable fields
100-
const fields: Array<{ name: string; type: string }> = [];
101-
const relations: Array<{ name: string }> = [];
102-
const fieldNames = new Set<string>();
103-
104-
Object.keys(attributes).forEach((key) => {
105-
const attr = attributes[key];
106-
107-
// Check if it's a relation
108-
if (attr.type === 'relation') {
109-
relations.push({ name: key });
110-
} else if (!fieldNames.has(key)) {
111-
// Regular field - add only once
112-
fieldNames.add(key);
113-
fields.push({
114-
name: key,
115-
type: attr.type || 'string'
116-
});
117-
}
118-
});
119-
120-
// Add default fields if not already present
121-
['id', 'createdAt', 'updatedAt'].forEach(defaultField => {
122-
if (!fieldNames.has(defaultField)) {
123-
fields.unshift({
124-
name: defaultField,
125-
type: defaultField === 'id' ? 'integer' : 'datetime'
126-
});
127-
}
128-
});
129-
130-
setAvailableFields(fields);
131-
setAvailableRelations(relations);
132-
133-
console.log('[AdvancedFilter] Extracted fields:', fields);
134-
console.log('[AdvancedFilter] Extracted relations:', relations);
135-
} else {
136-
console.warn('[AdvancedFilter] No attributes found in schema response');
137-
// Use fallback
138-
setAvailableFields([
139-
{ name: 'id', type: 'integer' },
140-
{ name: 'createdAt', type: 'datetime' },
141-
{ name: 'updatedAt', type: 'datetime' },
142-
]);
143-
}
144124
} catch (error) {
145125
console.error('[AdvancedFilter] Error fetching schema:', error);
146126
// Fallback to basic fields
147127
setAvailableFields([
148128
{ name: 'id', type: 'integer' },
129+
{ name: 'documentId', type: 'string' },
149130
{ name: 'createdAt', type: 'datetime' },
150131
{ name: 'updatedAt', type: 'datetime' },
151132
]);
@@ -178,8 +159,19 @@ const AdvancedFilterButton: React.FC = () => {
178159
cleanParams.set(key, value);
179160
});
180161

181-
// Navigate with new query
182-
navigate(`${currentPath}?${cleanParams.toString()}`);
162+
// Reset to page 1 when applying filters
163+
cleanParams.set('page', '1');
164+
165+
const finalUrl = `${currentPath}?${cleanParams.toString()}`;
166+
console.log('[AdvancedFilter] Final URL:', finalUrl);
167+
168+
// Navigate first, then reload to ensure Strapi CM picks up the filters
169+
navigate(finalUrl);
170+
171+
// Small delay then reload to ensure the URL is updated before reload
172+
setTimeout(() => {
173+
window.location.reload();
174+
}, 100);
183175
};
184176

185177
const handleClearFilters = () => {
@@ -194,7 +186,13 @@ const AdvancedFilterButton: React.FC = () => {
194186
}
195187
});
196188

197-
navigate(`${currentPath}${cleanParams.toString() ? '?' + cleanParams.toString() : ''}`);
189+
const finalUrl = `${currentPath}${cleanParams.toString() ? '?' + cleanParams.toString() : ''}`;
190+
navigate(finalUrl);
191+
192+
// Small delay then reload
193+
setTimeout(() => {
194+
window.location.reload();
195+
}, 100);
198196
};
199197

200198
// Extract current filters from URL
@@ -213,7 +211,7 @@ const AdvancedFilterButton: React.FC = () => {
213211
onClick={() => setShowModal(true)}
214212
size="S"
215213
>
216-
{hasActiveFilters ? '🔍 Filters Active' : 'Advanced Filters'}
214+
{hasActiveFilters ? 'Filters Active' : 'Advanced Filters'}
217215
</Button>
218216

219217
{hasActiveFilters && (

0 commit comments

Comments
 (0)