aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/settings/recipes/RecipesDashboard.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/settings/recipes/RecipesDashboard.jsx')
-rw-r--r--src/components/settings/recipes/RecipesDashboard.jsx309
1 files changed, 309 insertions, 0 deletions
diff --git a/src/components/settings/recipes/RecipesDashboard.jsx b/src/components/settings/recipes/RecipesDashboard.jsx
new file mode 100644
index 000000000..589b97ecd
--- /dev/null
+++ b/src/components/settings/recipes/RecipesDashboard.jsx
@@ -0,0 +1,309 @@
1import { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, injectIntl } from 'react-intl';
5import { NavLink } from 'react-router-dom';
6
7import injectSheet from 'react-jss';
8
9import { mdiOpenInNew } from '@mdi/js';
10import Button from '../../ui/button';
11import Input from '../../ui/input/index';
12import { H1, H2, H3 } from '../../ui/headline';
13import SearchInput from '../../ui/SearchInput';
14import Infobox from '../../ui/Infobox';
15import RecipeItem from './RecipeItem';
16import Loader from '../../ui/Loader';
17import Appear from '../../ui/effects/Appear';
18import { FERDIUM_SERVICE_REQUEST } from '../../../config';
19import RecipePreview from '../../../models/RecipePreview';
20import Icon from '../../ui/icon';
21
22const messages = defineMessages({
23 headline: {
24 id: 'settings.recipes.headline',
25 defaultMessage: 'Available services',
26 },
27 searchService: {
28 id: 'settings.searchService',
29 defaultMessage: 'Search service',
30 },
31 ferdiumPicksRecipes: {
32 id: 'settings.recipes.ferdiumPicks',
33 defaultMessage: 'Ferdium Picks',
34 },
35 allRecipes: {
36 id: 'settings.recipes.all',
37 defaultMessage: 'All services',
38 },
39 customRecipes: {
40 id: 'settings.recipes.custom',
41 defaultMessage: 'Custom Services',
42 },
43 nothingFound: {
44 id: 'settings.recipes.nothingFound',
45 defaultMessage:
46 'Sorry, but no service matched your search term - but you can still probably add it using the "Custom Website" option. Please note that the website might show more services that have been added to Ferdium since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdium.',
47 },
48 servicesSuccessfulAddedInfo: {
49 id: 'settings.recipes.servicesSuccessfulAddedInfo',
50 defaultMessage: 'Service successfully added',
51 },
52 missingService: {
53 id: 'settings.recipes.missingService',
54 defaultMessage: 'Missing a service?',
55 },
56 customRecipeIntro: {
57 id: 'settings.recipes.customService.intro',
58 defaultMessage:
59 'To add a custom service, copy the service recipe folder inside:',
60 },
61 openFolder: {
62 id: 'settings.recipes.customService.openFolder',
63 defaultMessage: 'Open folder',
64 },
65 openDevDocs: {
66 id: 'settings.recipes.customService.openDevDocs',
67 defaultMessage: 'Developer Documentation',
68 },
69 headlineCustomRecipes: {
70 id: 'settings.recipes.customService.headline.customRecipes',
71 defaultMessage: 'Custom 3rd Party Recipes',
72 },
73 headlineCommunityRecipes: {
74 id: 'settings.recipes.customService.headline.communityRecipes',
75 defaultMessage: 'Community 3rd Party Recipes',
76 },
77 headlineDevRecipes: {
78 id: 'settings.recipes.customService.headline.devRecipes',
79 defaultMessage: 'Your Development Service Recipes',
80 },
81});
82
83const styles = {
84 devRecipeIntroContainer: {
85 textAlign: 'center',
86 width: '100%',
87 height: 'auto',
88 margin: [40, 0],
89 },
90 path: {
91 marginTop: 20,
92
93 '& > div': {
94 fontFamily:
95 'SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace',
96 },
97 },
98 actionContainer: {
99 '& button': {
100 margin: [0, 10],
101 },
102 },
103 devRecipeList: {
104 marginTop: 20,
105 height: 'auto',
106 },
107 proBadge: {
108 marginLeft: '10px !important',
109 },
110};
111
112class RecipesDashboard extends Component {
113 static propTypes = {
114 recipes: MobxPropTypes.arrayOrObservableArray.isRequired,
115 customWebsiteRecipe: PropTypes.instanceOf(RecipePreview).isRequired,
116 isLoading: PropTypes.bool.isRequired,
117 hasLoadedRecipes: PropTypes.bool.isRequired,
118 showAddServiceInterface: PropTypes.func.isRequired,
119 searchRecipes: PropTypes.func.isRequired,
120 resetSearch: PropTypes.func.isRequired,
121 serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired,
122 searchNeedle: PropTypes.string,
123 recipeFilter: PropTypes.string,
124 recipeDirectory: PropTypes.string.isRequired,
125 openRecipeDirectory: PropTypes.func.isRequired,
126 openDevDocs: PropTypes.func.isRequired,
127 classes: PropTypes.object.isRequired,
128 };
129
130 static defaultProps = {
131 searchNeedle: '',
132 recipeFilter: 'all',
133 };
134
135 render() {
136 const {
137 recipes,
138 customWebsiteRecipe,
139 isLoading,
140 hasLoadedRecipes,
141 showAddServiceInterface,
142 searchRecipes,
143 resetSearch,
144 serviceStatus,
145 searchNeedle,
146 recipeFilter,
147 recipeDirectory,
148 openRecipeDirectory,
149 openDevDocs,
150 classes,
151 } = this.props;
152 const { intl } = this.props;
153
154 const communityRecipes = recipes.filter(r => !r.isDevRecipe);
155 const devRecipes = recipes.filter(r => r.isDevRecipe);
156
157 return (
158 <div className="settings__main">
159 <div className="settings__header">
160 <H1>{intl.formatMessage(messages.headline)}</H1>
161 </div>
162 <div className="settings__body recipes">
163 {serviceStatus.length > 0 && serviceStatus.includes('created') && (
164 <Appear>
165 <Infobox
166 type="success"
167 icon="checkbox-marked-circle-outline"
168 dismissable
169 >
170 {intl.formatMessage(messages.servicesSuccessfulAddedInfo)}
171 </Infobox>
172 </Appear>
173 )}
174 <SearchInput
175 placeholder={intl.formatMessage(messages.searchService)}
176 onChange={e => searchRecipes(e)}
177 onReset={() => resetSearch()}
178 autoFocus
179 throttle
180 />
181 <div className="recipes__navigation">
182 <NavLink
183 to="/settings/recipes"
184 className={() =>
185 recipeFilter === 'featured' ? 'badge badge--primary' : 'badge'
186 }
187 onClick={() => resetSearch()}
188 >
189 {intl.formatMessage(messages.ferdiumPicksRecipes)}
190 </NavLink>
191 <NavLink
192 to="/settings/recipes/all"
193 className={({ isActive }) =>
194 isActive && recipeFilter === 'all' ? 'badge badge--primary' : 'badge'
195 }
196 onClick={() => resetSearch()}
197 >
198 {intl.formatMessage(messages.allRecipes)}
199 </NavLink>
200 <NavLink
201 to="/settings/recipes/dev"
202 className={({ isActive }) =>
203 isActive && !searchNeedle ? 'badge badge--primary' : 'badge'
204 }
205 onClick={() => resetSearch()}
206 >
207 {intl.formatMessage(messages.customRecipes)}
208 </NavLink>
209 <a
210 href={FERDIUM_SERVICE_REQUEST}
211 target="_blank"
212 className="link recipes__service-request"
213 rel="noreferrer"
214 >
215 {intl.formatMessage(messages.missingService)}{' '}
216 <Icon icon={mdiOpenInNew} />
217 </a>
218 </div>
219 {/* )} */}
220 {isLoading ? (
221 <Loader />
222 ) : (
223 <>
224 {recipeFilter === 'dev' && (
225 <>
226 <H2>{intl.formatMessage(messages.headlineCustomRecipes)}</H2>
227 <div className={classes.devRecipeIntroContainer}>
228 <p>{intl.formatMessage(messages.customRecipeIntro)}</p>
229 <Input
230 value={recipeDirectory}
231 className={classes.path}
232 showLabel={false}
233 />
234 <div className={classes.actionContainer}>
235 <Button
236 onClick={openRecipeDirectory}
237 buttonType="secondary"
238 label={intl.formatMessage(messages.openFolder)}
239 />
240 <Button
241 onClick={openDevDocs}
242 buttonType="secondary"
243 label={intl.formatMessage(messages.openDevDocs)}
244 />
245 </div>
246 </div>
247 </>
248 )}
249 {recipeFilter === 'dev' && communityRecipes.length > 0 && (
250 <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3>
251 )}
252 <div className="recipes__list">
253 {communityRecipes.map(recipe => (
254 <RecipeItem
255 key={recipe.id}
256 recipe={recipe}
257 onClick={() =>
258 showAddServiceInterface({ recipeId: recipe.id })
259 }
260 />
261 ))}
262 </div>
263 {hasLoadedRecipes &&
264 recipes.length === 0 &&
265 recipeFilter !== 'dev' && (
266 <div className="align-middle settings__empty-state">
267 {customWebsiteRecipe && customWebsiteRecipe.id && (
268 <RecipeItem
269 key={customWebsiteRecipe.id}
270 recipe={customWebsiteRecipe}
271 onClick={() =>
272 showAddServiceInterface({
273 recipeId: customWebsiteRecipe.id,
274 })
275 }
276 />
277 )}
278 <p className="settings__empty-state-text">
279 {intl.formatMessage(messages.nothingFound)}
280 </p>
281 </div>
282 )}
283 {recipeFilter === 'dev' && devRecipes.length > 0 && (
284 <div className={classes.devRecipeList}>
285 <H3>{intl.formatMessage(messages.headlineDevRecipes)}</H3>
286 <div className="recipes__list">
287 {devRecipes.map(recipe => (
288 <RecipeItem
289 key={recipe.id}
290 recipe={recipe}
291 onClick={() =>
292 showAddServiceInterface({ recipeId: recipe.id })
293 }
294 />
295 ))}
296 </div>
297 </div>
298 )}
299 </>
300 )}
301 </div>
302 </div>
303 );
304 }
305}
306
307export default injectIntl(
308 injectSheet(styles, { injectTheme: true })(observer(RecipesDashboard)),
309);