diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/settings/recipes/RecipeItem.js | 2 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.js | 194 |
2 files changed, 159 insertions, 37 deletions
diff --git a/src/components/settings/recipes/RecipeItem.js b/src/components/settings/recipes/RecipeItem.js index 3bb0852b2..12e3775f6 100644 --- a/src/components/settings/recipes/RecipeItem.js +++ b/src/components/settings/recipes/RecipeItem.js | |||
@@ -19,7 +19,7 @@ export default @observer class RecipeItem extends Component { | |||
19 | className="recipe-teaser" | 19 | className="recipe-teaser" |
20 | onClick={onClick} | 20 | onClick={onClick} |
21 | > | 21 | > |
22 | {recipe.local && ( | 22 | {recipe.isDevRecipe && ( |
23 | <span className="recipe-teaser__dev-badge">dev</span> | 23 | <span className="recipe-teaser__dev-badge">dev</span> |
24 | )} | 24 | )} |
25 | <img | 25 | <img |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index 00cd725cf..e2e8121e5 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -4,12 +4,16 @@ import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | |||
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | 6 | ||
7 | import { Button, Input } from '@meetfranz/forms'; | ||
8 | import injectSheet from 'react-jss'; | ||
9 | import { H3, H2, ProBadge } from '@meetfranz/ui'; | ||
7 | import SearchInput from '../../ui/SearchInput'; | 10 | import SearchInput from '../../ui/SearchInput'; |
8 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/Infobox'; |
9 | import RecipeItem from './RecipeItem'; | 12 | import RecipeItem from './RecipeItem'; |
10 | import Loader from '../../ui/Loader'; | 13 | import Loader from '../../ui/Loader'; |
11 | import Appear from '../../ui/effects/Appear'; | 14 | import Appear from '../../ui/effects/Appear'; |
12 | import { FRANZ_SERVICE_REQUEST } from '../../../config'; | 15 | import { FRANZ_SERVICE_REQUEST } from '../../../config'; |
16 | import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | ||
13 | 17 | ||
14 | const messages = defineMessages({ | 18 | const messages = defineMessages({ |
15 | headline: { | 19 | headline: { |
@@ -28,9 +32,9 @@ const messages = defineMessages({ | |||
28 | id: 'settings.recipes.all', | 32 | id: 'settings.recipes.all', |
29 | defaultMessage: '!!!All services', | 33 | defaultMessage: '!!!All services', |
30 | }, | 34 | }, |
31 | devRecipes: { | 35 | customRecipes: { |
32 | id: 'settings.recipes.dev', | 36 | id: 'settings.recipes.custom', |
33 | defaultMessage: '!!!Development', | 37 | defaultMessage: '!!!Custom Services', |
34 | }, | 38 | }, |
35 | nothingFound: { | 39 | nothingFound: { |
36 | id: 'settings.recipes.nothingFound', | 40 | id: 'settings.recipes.nothingFound', |
@@ -44,9 +48,61 @@ const messages = defineMessages({ | |||
44 | id: 'settings.recipes.missingService', | 48 | id: 'settings.recipes.missingService', |
45 | defaultMessage: '!!!Missing a service?', | 49 | defaultMessage: '!!!Missing a service?', |
46 | }, | 50 | }, |
51 | customRecipeIntro: { | ||
52 | id: 'settings.recipes.customService.intro', | ||
53 | defaultMessage: '!!!To add a custom service, copy the recipe folder into:', | ||
54 | }, | ||
55 | openFolder: { | ||
56 | id: 'settings.recipes.customService.openFolder', | ||
57 | defaultMessage: '!!!Open directory', | ||
58 | }, | ||
59 | openDevDocs: { | ||
60 | id: 'settings.recipes.customService.openDevDocs', | ||
61 | defaultMessage: '!!!Developer Documentation', | ||
62 | }, | ||
63 | headlineCustomRecipes: { | ||
64 | id: 'settings.recipes.customService.headline.customRecipes', | ||
65 | defaultMessage: '!!!Custom Service Recipes', | ||
66 | }, | ||
67 | headlineCommunityRecipes: { | ||
68 | id: 'settings.recipes.customService.headline.communityRecipes', | ||
69 | defaultMessage: '!!!Community Services', | ||
70 | }, | ||
71 | headlineDevRecipes: { | ||
72 | id: 'settings.recipes.customService.headline.devRecipes', | ||
73 | defaultMessage: '!!!Your Development Service Recipes', | ||
74 | }, | ||
47 | }); | 75 | }); |
48 | 76 | ||
49 | export default @observer class RecipesDashboard extends Component { | 77 | const styles = { |
78 | devRecipeIntroContainer: { | ||
79 | textAlign: 'center', | ||
80 | width: '100%', | ||
81 | height: 'auto', | ||
82 | margin: [40, 0], | ||
83 | }, | ||
84 | path: { | ||
85 | marginTop: 20, | ||
86 | |||
87 | '& > div': { | ||
88 | fontFamily: 'SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace', | ||
89 | }, | ||
90 | }, | ||
91 | actionContainer: { | ||
92 | '& button': { | ||
93 | margin: [0, 10], | ||
94 | }, | ||
95 | }, | ||
96 | devRecipeList: { | ||
97 | marginTop: 20, | ||
98 | height: 'auto', | ||
99 | }, | ||
100 | proBadge: { | ||
101 | marginLeft: '10px !important', | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | export default @injectSheet(styles) @observer class RecipesDashboard extends Component { | ||
50 | static propTypes = { | 106 | static propTypes = { |
51 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, | 107 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, |
52 | isLoading: PropTypes.bool.isRequired, | 108 | isLoading: PropTypes.bool.isRequired, |
@@ -55,12 +111,19 @@ export default @observer class RecipesDashboard extends Component { | |||
55 | searchRecipes: PropTypes.func.isRequired, | 111 | searchRecipes: PropTypes.func.isRequired, |
56 | resetSearch: PropTypes.func.isRequired, | 112 | resetSearch: PropTypes.func.isRequired, |
57 | serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired, | 113 | serviceStatus: MobxPropTypes.arrayOrObservableArray.isRequired, |
58 | devRecipesCount: PropTypes.number.isRequired, | ||
59 | searchNeedle: PropTypes.string, | 114 | searchNeedle: PropTypes.string, |
115 | recipeFilter: PropTypes.string, | ||
116 | recipeDirectory: PropTypes.string.isRequired, | ||
117 | openRecipeDirectory: PropTypes.func.isRequired, | ||
118 | openDevDocs: PropTypes.func.isRequired, | ||
119 | classes: PropTypes.object.isRequired, | ||
120 | isCommunityRecipesPremiumFeature: PropTypes.bool.isRequired, | ||
121 | isUserPremiumUser: PropTypes.bool.isRequired, | ||
60 | }; | 122 | }; |
61 | 123 | ||
62 | static defaultProps = { | 124 | static defaultProps = { |
63 | searchNeedle: '', | 125 | searchNeedle: '', |
126 | recipeFilter: 'all', | ||
64 | } | 127 | } |
65 | 128 | ||
66 | static contextTypes = { | 129 | static contextTypes = { |
@@ -76,11 +139,21 @@ export default @observer class RecipesDashboard extends Component { | |||
76 | searchRecipes, | 139 | searchRecipes, |
77 | resetSearch, | 140 | resetSearch, |
78 | serviceStatus, | 141 | serviceStatus, |
79 | devRecipesCount, | ||
80 | searchNeedle, | 142 | searchNeedle, |
143 | recipeFilter, | ||
144 | recipeDirectory, | ||
145 | openRecipeDirectory, | ||
146 | openDevDocs, | ||
147 | classes, | ||
148 | isCommunityRecipesPremiumFeature, | ||
149 | isUserPremiumUser, | ||
81 | } = this.props; | 150 | } = this.props; |
82 | const { intl } = this.context; | 151 | const { intl } = this.context; |
83 | 152 | ||
153 | |||
154 | const communityRecipes = recipes.filter(r => !r.isDevRecipe); | ||
155 | const devRecipes = recipes.filter(r => r.isDevRecipe); | ||
156 | |||
84 | return ( | 157 | return ( |
85 | <div className="settings__main"> | 158 | <div className="settings__main"> |
86 | <div className="settings__header"> | 159 | <div className="settings__header"> |
@@ -122,20 +195,14 @@ export default @observer class RecipesDashboard extends Component { | |||
122 | > | 195 | > |
123 | {intl.formatMessage(messages.allRecipes)} | 196 | {intl.formatMessage(messages.allRecipes)} |
124 | </Link> | 197 | </Link> |
125 | {devRecipesCount > 0 && ( | 198 | <Link |
126 | <Link | 199 | to="/settings/recipes/dev" |
127 | to="/settings/recipes/dev" | 200 | className="badge" |
128 | className="badge" | 201 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} |
129 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} | 202 | onClick={() => resetSearch()} |
130 | onClick={() => resetSearch()} | 203 | > |
131 | > | 204 | {intl.formatMessage(messages.customRecipes)} |
132 | {intl.formatMessage(messages.devRecipes)} | 205 | </Link> |
133 | {' '} | ||
134 | ( | ||
135 | {devRecipesCount} | ||
136 | ) | ||
137 | </Link> | ||
138 | )} | ||
139 | <a href={FRANZ_SERVICE_REQUEST} target="_blank" className="link recipes__service-request"> | 206 | <a href={FRANZ_SERVICE_REQUEST} target="_blank" className="link recipes__service-request"> |
140 | {intl.formatMessage(messages.missingService)} | 207 | {intl.formatMessage(messages.missingService)} |
141 | {' '} | 208 | {' '} |
@@ -146,23 +213,78 @@ export default @observer class RecipesDashboard extends Component { | |||
146 | {isLoading ? ( | 213 | {isLoading ? ( |
147 | <Loader /> | 214 | <Loader /> |
148 | ) : ( | 215 | ) : ( |
149 | <div className="recipes__list"> | 216 | <> |
150 | {hasLoadedRecipes && recipes.length === 0 && ( | 217 | {recipeFilter === 'dev' && ( |
151 | <p className="align-middle settings__empty-state"> | 218 | <> |
152 | <span className="emoji"> | 219 | <H2> |
153 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | 220 | {intl.formatMessage(messages.headlineCustomRecipes)} |
154 | </span> | 221 | {!isUserPremiumUser && ( |
155 | {intl.formatMessage(messages.nothingFound)} | 222 | <ProBadge className={classes.proBadge} /> |
156 | </p> | 223 | )} |
224 | </H2> | ||
225 | <div className={classes.devRecipeIntroContainer}> | ||
226 | <p> | ||
227 | {intl.formatMessage(messages.customRecipeIntro)} | ||
228 | </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 | <PremiumFeatureContainer | ||
250 | condition={(recipeFilter === 'dev' && communityRecipes.length > 0) && isCommunityRecipesPremiumFeature} | ||
251 | > | ||
252 | {recipeFilter === 'dev' && communityRecipes.length > 0 && ( | ||
253 | <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3> | ||
254 | )} | ||
255 | <div className="recipes__list"> | ||
256 | {hasLoadedRecipes && recipes.length === 0 && recipeFilter !== 'dev'( | ||
257 | <p className="align-middle settings__empty-state"> | ||
258 | <span className="emoji"> | ||
259 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | ||
260 | </span> | ||
261 | {intl.formatMessage(messages.nothingFound)} | ||
262 | </p>, | ||
263 | )} | ||
264 | {communityRecipes.map(recipe => ( | ||
265 | <RecipeItem | ||
266 | key={recipe.id} | ||
267 | recipe={recipe} | ||
268 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
269 | /> | ||
270 | ))} | ||
271 | </div> | ||
272 | </PremiumFeatureContainer> | ||
273 | {recipeFilter === 'dev' && devRecipes.length > 0 && ( | ||
274 | <div className={classes.devRecipeList}> | ||
275 | <H3>{intl.formatMessage(messages.headlineDevRecipes)}</H3> | ||
276 | <div className="recipes__list"> | ||
277 | {devRecipes.map(recipe => ( | ||
278 | <RecipeItem | ||
279 | key={recipe.id} | ||
280 | recipe={recipe} | ||
281 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
282 | /> | ||
283 | ))} | ||
284 | </div> | ||
285 | </div> | ||
157 | )} | 286 | )} |
158 | {recipes.map(recipe => ( | 287 | </> |
159 | <RecipeItem | ||
160 | key={recipe.id} | ||
161 | recipe={recipe} | ||
162 | onClick={() => showAddServiceInterface({ recipeId: recipe.id })} | ||
163 | /> | ||
164 | ))} | ||
165 | </div> | ||
166 | )} | 288 | )} |
167 | </div> | 289 | </div> |
168 | </div> | 290 | </div> |