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