1
- import { AppMetadata } from '@mweb/engine'
1
+ import { AppMetadata , Document , useAppDocuments } from '@mweb/engine'
2
2
import React from 'react'
3
3
import styled from 'styled-components'
4
4
import { Image } from './image'
5
+ import { DocumentCard } from './document-card'
6
+ import { AppInMutation } from '@mweb/engine/lib/app/services/mutation/mutation.entity'
7
+ import { Spin } from 'antd'
5
8
6
- const Card = styled . div `
9
+ const Card = styled . div < { $backgroundColor ?: string } > `
7
10
position: relative;
8
11
width: 100%;
9
12
border-radius: 10px;
10
- background: #fff ;
13
+ background: ${ ( p ) => p . $backgroundColor } ;
11
14
border: 1px solid #eceef0;
12
- font-family: sans-serif;
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
16
+ 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
13
17
&:hover {
14
18
background: rgba(24, 121, 206, 0.1);
15
19
}
@@ -36,12 +40,20 @@ const CardContent = styled.div`
36
40
width: 100%;
37
41
`
38
42
39
- const TextLink = styled . div < { bold ?: boolean ; small ?: boolean ; ellipsis ?: boolean } > `
43
+ type TTextLink = {
44
+ bold ?: boolean
45
+ small ?: boolean
46
+ ellipsis ?: boolean
47
+ $color ?: string
48
+ }
49
+
50
+ const TextLink = styled . div < TTextLink > `
40
51
display: block;
41
52
margin: 0;
42
53
font-size: 14px;
43
54
line-height: 18px;
44
- color: ${ ( p ) => ( p . bold ? '#11181C !important' : '#687076 !important' ) } ;
55
+ color: ${ ( p ) =>
56
+ p . $color ? `${ p . $color } !important` : p . bold ? '#11181C !important' : '#687076 !important' } ;
45
57
font-weight: ${ ( p ) => ( p . bold ? '600' : '400' ) } ;
46
58
font-size: ${ ( p ) => ( p . small ? '12px' : '14px' ) } ;
47
59
overflow: ${ ( p ) => ( p . ellipsis ? 'hidden' : 'visible' ) } ;
@@ -50,29 +62,13 @@ const TextLink = styled.div<{ bold?: boolean; small?: boolean; ellipsis?: boolea
50
62
outline: none;
51
63
`
52
64
53
- // const Text = styled.p<{ bold?: boolean; small?: boolean; ellipsis?: boolean }>`
54
- // margin: 0;
55
- // font-size: 14px;
56
- // line-height: 20px;
57
- // color: ${(p) => (p.bold ? '#11181C' : '#687076')};
58
- // font-weight: ${(p) => (p.bold ? '600' : '400')};
59
- // font-size: ${(p) => (p.small ? '12px' : '14px')};
60
- // overflow: ${(p) => (p.ellipsis ? 'hidden' : '')};
61
- // text-overflow: ${(p) => (p.ellipsis ? 'ellipsis' : '')};
62
- // white-space: nowrap;
63
-
64
- // i {
65
- // margin-right: 3px;
66
- // }
67
- // `
68
-
69
- const Thumbnail = styled . div `
65
+ const Thumbnail = styled . div < { $shape : 'circle' | 'default' } > `
70
66
display: block;
71
67
width: 60px;
72
68
height: 60px;
73
69
flex-shrink: 0;
74
70
border: 1px solid #eceef0;
75
- border-radius: 8px;
71
+ border-radius: ${ ( props ) => ( props . $shape === 'circle' ? '99em' : ' 8px' ) } ;
76
72
overflow: hidden;
77
73
outline: none;
78
74
transition: border-color 200ms;
@@ -108,6 +104,45 @@ const ButtonLink = styled.button`
108
104
}
109
105
`
110
106
107
+ const DocumentsWrapper = styled . div `
108
+ display: flex;
109
+ padding-bottom: 10px;
110
+ `
111
+
112
+ const SideLine = styled . div `
113
+ border: 1px solid #c1c6ce;
114
+ margin: 0 10px;
115
+ `
116
+
117
+ const DocumentCardList = styled . div `
118
+ width: 100%;
119
+ margin-right: 10px;
120
+ display: flex;
121
+ flex-direction: column;
122
+ gap: 6px;
123
+ `
124
+
125
+ const MoreIcon = ( ) => (
126
+ < svg width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
127
+ < path
128
+ d = "M10 8L14 12L10 16"
129
+ stroke = "#7A818B"
130
+ strokeWidth = "1.5"
131
+ strokeLinecap = "round"
132
+ strokeLinejoin = "round"
133
+ />
134
+ < rect
135
+ x = "3.75"
136
+ y = "3.75"
137
+ width = "16.5"
138
+ height = "16.5"
139
+ rx = "3.25"
140
+ stroke = "#7A818B"
141
+ strokeWidth = "1.5"
142
+ />
143
+ </ svg >
144
+ )
145
+
111
146
const UncheckedIcon = ( ) => (
112
147
< svg xmlns = "http://www.w3.org/2000/svg" width = "24" height = "24" viewBox = "0 0 24 24" fill = "none" >
113
148
< rect
@@ -145,27 +180,54 @@ const CheckedIcon = () => (
145
180
</ svg >
146
181
)
147
182
148
- export interface Props {
183
+ export interface ISimpleApplicationCardProps {
149
184
src : string
150
185
metadata : AppMetadata [ 'metadata' ]
186
+ disabled : boolean
151
187
isChecked : boolean
152
188
onChange : ( isChecked : boolean ) => void
189
+ iconShape ?: 'circle'
190
+ textColor ?: string
191
+ backgroundColor ?: string
192
+ }
193
+
194
+ export interface IApplicationCardWithDocsProps {
195
+ src : string
196
+ metadata : AppMetadata [ 'metadata' ]
153
197
disabled : boolean
198
+ docsIds : AppInMutation [ 'documentId' ] [ ]
199
+ onDocCheckboxChange : ( docId : string | null , isChecked : boolean ) => void
200
+ onOpenDocumentsModal : ( docs : Document [ ] ) => void
201
+ }
202
+
203
+ interface IApplicationCard
204
+ extends ISimpleApplicationCardProps ,
205
+ Omit < IApplicationCardWithDocsProps , 'docsIds' > {
206
+ hasDocuments : boolean
207
+ usingDocs : ( Document | null ) [ ]
208
+ allDocs : Document [ ]
154
209
}
155
210
156
- export const ApplicationCard : React . FC < Props > = ( {
211
+ const ApplicationCard : React . FC < IApplicationCard > = ( {
157
212
src,
158
213
metadata,
214
+ disabled,
215
+ hasDocuments,
216
+ iconShape,
217
+ textColor,
218
+ backgroundColor,
159
219
isChecked,
220
+ usingDocs,
221
+ allDocs,
160
222
onChange,
161
- disabled,
223
+ onDocCheckboxChange,
224
+ onOpenDocumentsModal,
162
225
} ) => {
163
- const [ accountId , , widgetName ] = src . split ( '/' )
164
-
226
+ const [ accountId , , appId ] = src . split ( '/' )
165
227
return (
166
- < Card className = { disabled ? 'disabled' : '' } >
228
+ < Card $backgroundColor = { backgroundColor ?? 'white' } className = { disabled ? 'disabled' : '' } >
167
229
< CardBody >
168
- < Thumbnail >
230
+ < Thumbnail $shape = { iconShape ?? 'default' } >
169
231
< Image
170
232
image = { metadata . image }
171
233
fallbackUrl = "https://ipfs.near.social/ipfs/bafkreifc4burlk35hxom3klq4mysmslfirj7slueenbj7ddwg7pc6ixomu"
@@ -174,22 +236,76 @@ export const ApplicationCard: React.FC<Props> = ({
174
236
</ Thumbnail >
175
237
176
238
< CardContent >
177
- < TextLink bold ellipsis >
178
- { metadata . name || widgetName }
239
+ < TextLink $color = { textColor } bold ellipsis >
240
+ { metadata . name || appId }
179
241
</ TextLink >
180
242
181
243
< TextLink small ellipsis >
182
244
@{ accountId }
183
245
</ TextLink >
184
246
</ CardContent >
247
+
185
248
< ButtonLink
186
249
className = { disabled ? 'disabled' : '' }
187
250
disabled = { disabled }
188
- onClick = { ( ) => onChange ( ! isChecked ) }
251
+ onClick = { hasDocuments ? ( ) => onOpenDocumentsModal ( allDocs ) : ( ) => onChange ( ! isChecked ) }
189
252
>
190
- { isChecked ? < CheckedIcon /> : < UncheckedIcon /> }
253
+ { hasDocuments ? < MoreIcon /> : isChecked ? < CheckedIcon /> : < UncheckedIcon /> }
191
254
</ ButtonLink >
192
255
</ CardBody >
256
+
257
+ { hasDocuments && usingDocs . length ? (
258
+ < DocumentsWrapper >
259
+ < SideLine />
260
+ < DocumentCardList >
261
+ { usingDocs . map ( ( doc ) => (
262
+ < DocumentCard
263
+ key = { doc ?. id || 'empty' }
264
+ src = { doc ?. id ?? null }
265
+ metadata = { doc ?. metadata ?? null }
266
+ onChange = { ( ) => onDocCheckboxChange ( doc ?. id ?? null , false ) }
267
+ disabled = { disabled }
268
+ appMetadata = { metadata }
269
+ />
270
+ ) ) }
271
+ </ DocumentCardList >
272
+ </ DocumentsWrapper >
273
+ ) : null }
193
274
</ Card >
194
275
)
195
276
}
277
+
278
+ export const SimpleApplicationCard : React . FC < ISimpleApplicationCardProps > = ( props ) => (
279
+ < ApplicationCard
280
+ { ...props }
281
+ hasDocuments = { false }
282
+ onOpenDocumentsModal = { ( ) => null }
283
+ onDocCheckboxChange = { ( ) => null }
284
+ usingDocs = { [ ] }
285
+ allDocs = { [ ] }
286
+ />
287
+ )
288
+
289
+ export const ApplicationCardWithDocs : React . FC < IApplicationCardWithDocsProps > = ( props ) => {
290
+ const { src, docsIds } = props
291
+ const { documents, isLoading } = useAppDocuments ( src )
292
+ const usingDocs : ( Document | null ) [ ] = documents ?. filter ( ( doc ) => docsIds . includes ( doc . id ) )
293
+ if ( docsIds . includes ( null ) ) usingDocs . unshift ( null )
294
+
295
+ return isLoading ? (
296
+ < Card >
297
+ < CardBody >
298
+ < Spin style = { { width : '100%' } } />
299
+ </ CardBody >
300
+ </ Card >
301
+ ) : (
302
+ < ApplicationCard
303
+ { ...props }
304
+ hasDocuments = { true }
305
+ isChecked = { false }
306
+ onChange = { ( ) => null }
307
+ usingDocs = { usingDocs }
308
+ allDocs = { documents }
309
+ />
310
+ )
311
+ }
0 commit comments