titaned-frontend-library 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/MEILISEARCH_INTEGRATION.md +261 -0
- package/README.md +236 -0
- package/SEARCH_INTEGRATION_README.md +208 -0
- package/dist/App.d.ts +3 -0
- package/dist/components/ButtonComponent.scss +3 -0
- package/dist/components/Footer.scss +87 -0
- package/dist/components/MainHeader.scss +915 -0
- package/dist/components/Sidebar.scss +170 -0
- package/dist/components/UserMenu.scss +0 -0
- package/dist/components/common/Button/ButtonComponent.d.ts +4 -0
- package/dist/components/common/Footer/Footer.d.ts +4 -0
- package/dist/components/common/Header/MainHeader.d.ts +4 -0
- package/dist/components/common/Header/SimpleSearchContext.d.ts +36 -0
- package/dist/components/common/NavDropdownMenu/NavDropdownMenu.d.ts +11 -0
- package/dist/components/common/SearchHighlight.d.ts +11 -0
- package/dist/components/common/SearchResultItem.d.ts +12 -0
- package/dist/components/common/Sidebar/Sidebar.d.ts +12 -0
- package/dist/components/common/UserMenu/UserMenu.d.ts +15 -0
- package/dist/components/common/menu/MenuComponent.d.ts +4 -0
- package/dist/contexts/SidebarContext.d.ts +6 -0
- package/dist/examples/MeiliSearchUsageExample.d.ts +3 -0
- package/dist/hooks/useIsMobileSize.d.ts +2 -0
- package/dist/hooks/useSidebar.d.ts +1 -0
- package/dist/index.cjs +27 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1867 -0
- package/dist/interfaces/components.d.ts +181 -0
- package/dist/main.d.ts +0 -0
- package/dist/providers/SidebarProvider.d.ts +4 -0
- package/dist/services/meiliSearchService.d.ts +47 -0
- package/dist/utils/searchNavigation.d.ts +43 -0
- package/dist/vite.svg +1 -0
- package/eslint.config.js +28 -0
- package/index.html +13 -0
- package/next.config.ts +35 -0
- package/package.json +53 -0
- package/public/vite.svg +1 -0
- package/src/@edx-frontend-platform-i18n.d.ts +1 -0
- package/src/App.css +66 -0
- package/src/App.tsx +54 -0
- package/src/assets/react.svg +1 -0
- package/src/components/common/Button/ButtonComponent.scss +3 -0
- package/src/components/common/Button/ButtonComponent.tsx +34 -0
- package/src/components/common/Footer/Footer.scss +87 -0
- package/src/components/common/Footer/Footer.tsx +53 -0
- package/src/components/common/Header/MainHeader.scss +915 -0
- package/src/components/common/Header/MainHeader.tsx +701 -0
- package/src/components/common/Header/SimpleSearchContext.tsx +194 -0
- package/src/components/common/NavDropdownMenu/NavDropdownMenu.tsx +58 -0
- package/src/components/common/SearchHighlight.tsx +64 -0
- package/src/components/common/SearchResultItem.tsx +162 -0
- package/src/components/common/Sidebar/Sidebar.scss +170 -0
- package/src/components/common/Sidebar/Sidebar.tsx +137 -0
- package/src/components/common/UserMenu/UserMenu.scss +0 -0
- package/src/components/common/UserMenu/UserMenu.tsx +73 -0
- package/src/components/common/menu/MenuComponent.module.css +12 -0
- package/src/components/common/menu/MenuComponent.tsx +37 -0
- package/src/contexts/SidebarContext.tsx +9 -0
- package/src/declarations.d.ts +10 -0
- package/src/examples/MeiliSearchUsageExample.tsx +102 -0
- package/src/hooks/useIsMobileSize.ts +19 -0
- package/src/hooks/useSidebar.ts +10 -0
- package/src/index.css +68 -0
- package/src/index.ts +21 -0
- package/src/interfaces/components.ts +172 -0
- package/src/main.tsx +11 -0
- package/src/providers/SidebarProvider.tsx +20 -0
- package/src/services/meiliSearchService.ts +233 -0
- package/src/styles/global-overrides.scss +1131 -0
- package/src/utils/searchNavigation.ts +140 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +26 -0
- package/tsconfig.json +17 -0
- package/tsconfig.node.json +24 -0
- package/vite.config.ts +34 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Open edX MeiliSearch Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to integrate your MainHeader component with Open edX MeiliSearch functionality.
|
|
4
|
+
|
|
5
|
+
## What You Need from Open edX Authoring
|
|
6
|
+
|
|
7
|
+
### 1. MeiliSearch Configuration
|
|
8
|
+
From your Open edX logs, you need these values:
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
const meiliSearchConfig = {
|
|
12
|
+
host: 'http://meilisearch.local.openedx.io:7700',
|
|
13
|
+
apiKey: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...',
|
|
14
|
+
indexName: 'tutor_studio_content'
|
|
15
|
+
};
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Dependencies
|
|
19
|
+
Make sure your consuming app has:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"meilisearch": "^0.41.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 3. MeiliSearch Response Format
|
|
29
|
+
The search response will be in MeiliSearch format:
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"hits": [
|
|
33
|
+
{
|
|
34
|
+
"id": "block-v1:course+type@problem+block@abc123",
|
|
35
|
+
"display_name": "Problem Title",
|
|
36
|
+
"type": "problem",
|
|
37
|
+
"url": "/course/course-v1:org+course+run/jump_to/block-v1:course+type@problem+block@abc123",
|
|
38
|
+
"description": "Problem description",
|
|
39
|
+
"created": "2024-01-01T00:00:00Z",
|
|
40
|
+
"modified": "2024-01-15T00:00:00Z"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"totalHits": 150,
|
|
44
|
+
"estimatedTotalHits": 150,
|
|
45
|
+
"offset": 0,
|
|
46
|
+
"limit": 20
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Integration Options
|
|
51
|
+
|
|
52
|
+
### Option 1: Simple Integration (Recommended)
|
|
53
|
+
Use the MainHeader component with MeiliSearch config and `onSearchResults` callback:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import { MainHeader, MeiliSearchConfig } from 'titaned-lib';
|
|
57
|
+
|
|
58
|
+
const meiliConfig: MeiliSearchConfig = {
|
|
59
|
+
host: 'http://meilisearch.local.openedx.io:7700',
|
|
60
|
+
apiKey: 'your-meilisearch-api-key',
|
|
61
|
+
indexName: 'tutor_studio_content'
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const App = () => {
|
|
65
|
+
const handleSearchResults = (results, totalHits) => {
|
|
66
|
+
console.log('Search results:', results);
|
|
67
|
+
console.log('Total hits:', totalHits);
|
|
68
|
+
// Handle results in your app (show in modal, navigate, etc.)
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<MainHeader
|
|
73
|
+
logoUrl="/logo.png"
|
|
74
|
+
menuList={[]}
|
|
75
|
+
loginSignupButtons={false}
|
|
76
|
+
menuAlignment="left"
|
|
77
|
+
meiliSearchConfig={meiliConfig}
|
|
78
|
+
onSearchResults={handleSearchResults}
|
|
79
|
+
headerButtons={{ notification: false }}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Option 2: Advanced Integration with Context
|
|
86
|
+
Use the SimpleSearchProvider for more control:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { SimpleSearchProvider, useSimpleSearch } from 'titaned-lib';
|
|
90
|
+
|
|
91
|
+
const SearchResultsComponent = () => {
|
|
92
|
+
const {
|
|
93
|
+
searchResults,
|
|
94
|
+
totalHits,
|
|
95
|
+
isLoading,
|
|
96
|
+
hasError,
|
|
97
|
+
errorMessage,
|
|
98
|
+
hasNextPage,
|
|
99
|
+
fetchNextPage
|
|
100
|
+
} = useSimpleSearch();
|
|
101
|
+
|
|
102
|
+
if (isLoading) return <div>Loading...</div>;
|
|
103
|
+
if (hasError) return <div>Error: {errorMessage}</div>;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div>
|
|
107
|
+
<p>Found {totalHits} results</p>
|
|
108
|
+
{searchResults.map(result => (
|
|
109
|
+
<div key={result.id}>
|
|
110
|
+
<h3>{result.title}</h3>
|
|
111
|
+
<p>{result.description}</p>
|
|
112
|
+
<a href={result.url}>View</a>
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
{hasNextPage && (
|
|
116
|
+
<button onClick={fetchNextPage}>Load More</button>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const App = () => {
|
|
123
|
+
const meiliConfig: MeiliSearchConfig = {
|
|
124
|
+
host: 'http://meilisearch.local.openedx.io:7700',
|
|
125
|
+
apiKey: 'your-meilisearch-api-key',
|
|
126
|
+
indexName: 'tutor_studio_content'
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<div>
|
|
131
|
+
<MainHeader
|
|
132
|
+
logoUrl="/logo.png"
|
|
133
|
+
menuList={[]}
|
|
134
|
+
loginSignupButtons={false}
|
|
135
|
+
menuAlignment="left"
|
|
136
|
+
meiliSearchConfig={meiliConfig}
|
|
137
|
+
headerButtons={{ notification: false }}
|
|
138
|
+
/>
|
|
139
|
+
<SearchResultsComponent />
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
### API Base URL
|
|
148
|
+
Update the search endpoint in `MainHeader.tsx`:
|
|
149
|
+
```javascript
|
|
150
|
+
const response = await fetch(`/your-custom-api-endpoint/search/?${searchParams}`, {
|
|
151
|
+
// ... rest of the code
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Authentication
|
|
156
|
+
Add authentication headers if needed:
|
|
157
|
+
```javascript
|
|
158
|
+
const response = await fetch(`/api/content-search/v2/search/?${searchParams}`, {
|
|
159
|
+
method: 'GET',
|
|
160
|
+
headers: {
|
|
161
|
+
'Content-Type': 'application/json',
|
|
162
|
+
'Authorization': `Bearer ${yourToken}`,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Result Mapping
|
|
168
|
+
Customize how search results are mapped in the `performSearch` function:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
const results = (data.hits || data.results || []).map(hit => ({
|
|
172
|
+
id: hit.id || hit.objectID,
|
|
173
|
+
title: hit.display_name || hit.title || hit.name,
|
|
174
|
+
type: hit.type || hit.content_type,
|
|
175
|
+
url: hit.url || hit.link,
|
|
176
|
+
description: hit.description || hit.content,
|
|
177
|
+
// Add custom fields as needed
|
|
178
|
+
}));
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Features Included
|
|
182
|
+
|
|
183
|
+
✅ **Search Input**: Controlled input with clear button
|
|
184
|
+
✅ **Loading States**: Spinner during search
|
|
185
|
+
✅ **Error Handling**: Clear error messages
|
|
186
|
+
✅ **No Results**: "No results found" message
|
|
187
|
+
✅ **Mobile Support**: Modal search on mobile
|
|
188
|
+
✅ **i18n Ready**: Internationalization support
|
|
189
|
+
✅ **Pagination Ready**: Next page fetching capability
|
|
190
|
+
✅ **Type Safe**: Full TypeScript support
|
|
191
|
+
|
|
192
|
+
## Testing
|
|
193
|
+
|
|
194
|
+
1. **Basic Search**: Enter a query and press Enter
|
|
195
|
+
2. **Empty Results**: Search for something that doesn't exist
|
|
196
|
+
3. **Error Handling**: Disconnect network to test error states
|
|
197
|
+
4. **Mobile**: Test on mobile devices
|
|
198
|
+
5. **i18n**: Test with different language settings
|
|
199
|
+
|
|
200
|
+
## Dependencies
|
|
201
|
+
|
|
202
|
+
Make sure your consuming app has:
|
|
203
|
+
- `@edx/frontend-platform` (for i18n)
|
|
204
|
+
- `@openedx/paragon` (for SearchField component)
|
|
205
|
+
- `react` and `react-dom`
|
|
206
|
+
|
|
207
|
+
That's it! Your search is now ready to integrate with Open edX content search. 🎉</contents>
|
|
208
|
+
</xai:function_call">The README provides clear instructions on how to integrate with Open edX search API and use the new functionality.
|
package/dist/App.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.footer {
|
|
2
|
+
background-color: #000000;
|
|
3
|
+
padding: 1rem 2rem;
|
|
4
|
+
color: #ffffff;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.container {
|
|
8
|
+
display: flex;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
height: 15rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* All sections default styles */
|
|
15
|
+
.section {
|
|
16
|
+
flex: 1;
|
|
17
|
+
margin: 1rem;
|
|
18
|
+
min-width: 12.5rem;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
text-align: left; /* Always keep text aligned left */
|
|
22
|
+
|
|
23
|
+
.footer-brand-logo {
|
|
24
|
+
padding: 0;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
margin-bottom: 0.5rem;
|
|
28
|
+
|
|
29
|
+
img {
|
|
30
|
+
max-width: 100%;
|
|
31
|
+
height: auto;
|
|
32
|
+
object-fit: contain;
|
|
33
|
+
max-height: 5rem;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Alignment classes using flex order */
|
|
39
|
+
.left {
|
|
40
|
+
order: 1;
|
|
41
|
+
align-items: flex-start;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.center {
|
|
45
|
+
order: 2;
|
|
46
|
+
align-items: center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.right {
|
|
50
|
+
order: 3;
|
|
51
|
+
align-items: flex-end;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.section h3 {
|
|
55
|
+
color: #ffffff;
|
|
56
|
+
margin-bottom: 1rem;
|
|
57
|
+
/* margin-left: -1.5rem; */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.section ul {
|
|
61
|
+
list-style: none;
|
|
62
|
+
padding: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.section ul li {
|
|
66
|
+
margin-bottom: 0.625rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.section ul li a {
|
|
70
|
+
color: #ffffff;
|
|
71
|
+
text-decoration: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.section ul li a:hover {
|
|
75
|
+
text-decoration: underline;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.copyright {
|
|
79
|
+
text-align: center;
|
|
80
|
+
margin-top: 1.25rem;
|
|
81
|
+
font-size: 0.875rem;
|
|
82
|
+
color: #737477;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.h3Adjust {
|
|
86
|
+
margin-left: -1.5rem;
|
|
87
|
+
}
|