1
1
import { makeStyles } from "@material-ui/core/styles"
2
+ import { TemplateExample } from "api/typesGenerated"
2
3
import { AlertBanner } from "components/AlertBanner/AlertBanner"
3
4
import { Maybe } from "components/Conditionals/Maybe"
4
5
import { Loader } from "components/Loader/Loader"
@@ -8,11 +9,42 @@ import {
8
9
PageHeaderSubtitle ,
9
10
PageHeaderTitle ,
10
11
} from "components/PageHeader/PageHeader"
12
+ import { Stack } from "components/Stack/Stack"
11
13
import { FC } from "react"
12
14
import { useTranslation } from "react-i18next"
13
15
import { Link } from "react-router-dom"
14
16
import { StarterTemplatesContext } from "xServices/starterTemplates/starterTemplatesXService"
15
17
18
+ const getTagLabel = ( tag : string ) => {
19
+ const labelByTag : Record < string , string > = {
20
+ digitalocean : "Digital Ocean" ,
21
+ aws : "AWS" ,
22
+ google : "Google Cloud" ,
23
+ }
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- this can be undefined
26
+ return labelByTag [ tag ] ?? tag
27
+ }
28
+
29
+ const getTemplatesByTag = ( starterTemplates : TemplateExample [ ] | undefined ) => {
30
+ if ( ! starterTemplates ) {
31
+ return
32
+ }
33
+
34
+ const tags : Record < string , TemplateExample [ ] > = { }
35
+ starterTemplates . forEach ( ( template ) => {
36
+ template . tags . forEach ( ( tag ) => {
37
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- this can be undefined
38
+ if ( tags [ tag ] ) {
39
+ tags [ tag ] . push ( template )
40
+ } else {
41
+ tags [ tag ] = [ template ]
42
+ }
43
+ } )
44
+ } )
45
+ return tags
46
+ }
47
+
16
48
export interface StarterTemplatesPageViewProps {
17
49
context : StarterTemplatesContext
18
50
}
@@ -22,6 +54,10 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
22
54
} ) => {
23
55
const { t } = useTranslation ( "starterTemplatesPage" )
24
56
const styles = useStyles ( )
57
+ const templatesByTag = getTemplatesByTag ( context . starterTemplates )
58
+ const tags = templatesByTag
59
+ ? Object . keys ( templatesByTag ) . sort ( ( a , b ) => a . localeCompare ( b ) )
60
+ : undefined
25
61
26
62
return (
27
63
< Margins >
@@ -38,27 +74,70 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
38
74
< Loader />
39
75
</ Maybe >
40
76
41
- < div className = { styles . templates } >
42
- { context . starterTemplates &&
43
- context . starterTemplates . map ( ( example ) => (
44
- < Link to = { example . id } className = { styles . template } key = { example . id } >
45
- < div className = { styles . templateIcon } >
46
- < img src = { example . icon } alt = "" />
47
- </ div >
48
- < div className = { styles . templateInfo } >
49
- < span className = { styles . templateName } > { example . name } </ span >
50
- < span className = { styles . templateDescription } >
51
- { example . description }
52
- </ span >
53
- </ div >
77
+ < Stack direction = "row" spacing = { 4 } >
78
+ { templatesByTag && tags && (
79
+ < Stack className = { styles . filter } >
80
+ < span className = { styles . filterCaption } > Filter</ span >
81
+ < Link to = "" className = { styles . tagLink } >
82
+ All templates ({ context . starterTemplates ?. length } )
54
83
</ Link >
55
- ) ) }
56
- </ div >
84
+ { tags . map ( ( tag ) => (
85
+ < Link key = { tag } to = { `?tag=${ tag } ` } className = { styles . tagLink } >
86
+ { getTagLabel ( tag ) } ({ templatesByTag [ tag ] . length } )
87
+ </ Link >
88
+ ) ) }
89
+ </ Stack >
90
+ ) }
91
+
92
+ < div className = { styles . templates } >
93
+ { context . starterTemplates &&
94
+ context . starterTemplates . map ( ( example ) => (
95
+ < Link
96
+ to = { example . id }
97
+ className = { styles . template }
98
+ key = { example . id }
99
+ >
100
+ < div className = { styles . templateIcon } >
101
+ < img src = { example . icon } alt = "" />
102
+ </ div >
103
+ < div className = { styles . templateInfo } >
104
+ < span className = { styles . templateName } > { example . name } </ span >
105
+ < span className = { styles . templateDescription } >
106
+ { example . description }
107
+ </ span >
108
+ </ div >
109
+ </ Link >
110
+ ) ) }
111
+ </ div >
112
+ </ Stack >
57
113
</ Margins >
58
114
)
59
115
}
60
116
61
117
const useStyles = makeStyles ( ( theme ) => ( {
118
+ filter : {
119
+ width : theme . spacing ( 26 ) ,
120
+ } ,
121
+
122
+ filterCaption : {
123
+ textTransform : "uppercase" ,
124
+ fontWeight : 600 ,
125
+ fontSize : 12 ,
126
+ color : theme . palette . text . secondary ,
127
+ letterSpacing : "0.1em" ,
128
+ } ,
129
+
130
+ tagLink : {
131
+ color : theme . palette . text . secondary ,
132
+ textDecoration : "none" ,
133
+ fontSize : 14 ,
134
+ textTransform : "capitalize" ,
135
+
136
+ "&:hover" : {
137
+ color : theme . palette . text . primary ,
138
+ } ,
139
+ } ,
140
+
62
141
templates : {
63
142
display : "grid" ,
64
143
gridTemplateColumns : "repeat(2, minmax(0, 1fr))" ,
@@ -85,6 +164,7 @@ const useStyles = makeStyles((theme) => ({
85
164
display : "flex" ,
86
165
alignItems : "center" ,
87
166
justifyContent : "center" ,
167
+ flexShrink : 0 ,
88
168
89
169
"& img" : {
90
170
height : theme . spacing ( 4 ) ,
@@ -96,6 +176,7 @@ const useStyles = makeStyles((theme) => ({
96
176
display : "flex" ,
97
177
flexDirection : "column" ,
98
178
gap : theme . spacing ( 0.5 ) ,
179
+ overflow : "hidden" ,
99
180
} ,
100
181
101
182
templateName : {
@@ -105,5 +186,9 @@ const useStyles = makeStyles((theme) => ({
105
186
templateDescription : {
106
187
fontSize : theme . spacing ( 1.75 ) ,
107
188
color : theme . palette . text . secondary ,
189
+ textOverflow : "ellipsis" ,
190
+ width : "100%" ,
191
+ overflow : "hidden" ,
192
+ whiteSpace : "nowrap" ,
108
193
} ,
109
194
} ) )
0 commit comments