11/// <cts-enable />
2- import {
3- derive ,
4- handler ,
5- JSONSchema ,
6- lift ,
7- Mutable ,
8- NAME ,
9- recipe ,
10- Schema ,
11- UI ,
12- } from "commontools" ;
2+ import { Cell , Default , derive , handler , NAME , recipe , UI } from "commontools" ;
133
144const DISABLED_VIA_UI = "Disabled via UI" ;
155
16- // NOTE(ja): this must be the same as the schema in background-charm-service/src/schema.ts
17- const BGCharmEntrySchema = {
18- type : "object" ,
19- properties : {
20- space : { type : "string" } ,
21- charmId : { type : "string" } ,
22- integration : { type : "string" } ,
23- createdAt : { type : "number" } ,
24- updatedAt : { type : "number" } ,
25- disabledAt : { type : "number" , default : 0 } ,
26- lastRun : { type : "number" , default : 0 } ,
27- status : { type : "string" , default : "" } ,
28- } ,
29- required : [
30- "space" ,
31- "charmId" ,
32- "integration" ,
33- "createdAt" ,
34- "updatedAt" ,
35- "lastRun" ,
36- "status" ,
37- ] ,
38- } as const satisfies JSONSchema ;
39- type BGCharmEntry = Mutable < Schema < typeof BGCharmEntrySchema > > ;
40-
41- const BGCharmEntriesSchema = {
42- type : "array" ,
43- items : BGCharmEntrySchema ,
44- default : [ ] ,
45- } as const satisfies JSONSchema ;
46- type BGCharmEntries = Schema < typeof BGCharmEntriesSchema > ;
47-
48- const InputSchema = {
49- type : "object" ,
50- properties : {
51- charms : BGCharmEntriesSchema ,
52- } ,
53- } as const satisfies JSONSchema ;
54-
55- const ResultSchema = {
56- type : "object" ,
57- properties : {
58- charms : {
59- type : "array" ,
60- items : BGCharmEntrySchema ,
61- } ,
62- } ,
63- } as const satisfies JSONSchema ;
6+ type BGCharmEntry = {
7+ space : string ;
8+ charmId : string ;
9+ integration : string ;
10+ createdAt : number ;
11+ updatedAt : number ;
12+ disabledAt : Default < number , 0 > ;
13+ lastRun : Default < number , 0 > ;
14+ status : Default < string , "" > ;
15+ } ;
16+
17+ type InputSchema = {
18+ charms : Default < BGCharmEntry [ ] , [ ] > ;
19+ } ;
20+
21+ type ResultSchema = {
22+ charms : BGCharmEntry [ ] ;
23+ } ;
6424
6525const deleteCharm = handler <
6626 never ,
67- { charms : BGCharmEntry [ ] ; charm : BGCharmEntry }
27+ { charms : Cell < BGCharmEntry [ ] > ; charm : Cell < BGCharmEntry > }
6828> (
6929 ( _ , { charm, charms } ) => {
70- const idx = charms . findIndex ( ( i ) =>
71- i . space === charm . space && i . charmId === charm . charmId
30+ const { space, charmId } = charm . get ( ) ;
31+ const newList = charms . get ( ) . slice ( ) ;
32+ const index = newList . findIndex ( ( i ) =>
33+ i . space === space && i . charmId === charmId
7234 ) ;
73- if ( idx !== - 1 ) charms . splice ( idx , 1 ) ;
35+ if ( index >= 0 && index < newList . length ) {
36+ newList . splice ( index , 1 ) ;
37+ charms . set ( newList ) ;
38+ }
7439 } ,
7540) ;
7641
77- const toggleCharm = handler < never , { charm : BGCharmEntry } > ( ( _ , { charm } ) => {
78- if ( charm . disabledAt ) {
79- charm . disabledAt = undefined ;
80- charm . status = "Initializing..." ;
81- } else {
82- charm . disabledAt = Date . now ( ) ;
83- charm . status = DISABLED_VIA_UI ;
84- }
85- } ) ;
42+ const toggleCharm = handler < never , { charm : Cell < BGCharmEntry > } > (
43+ ( _ , { charm } ) => {
44+ const data = charm . get ( ) ;
45+ if ( data . disabledAt ) {
46+ charm . set ( { ...data , disabledAt : 0 , status : "Initializing..." } ) ;
47+ } else {
48+ charm . set ( { ...data , disabledAt : Date . now ( ) , status : DISABLED_VIA_UI } ) ;
49+ }
50+ } ,
51+ ) ;
8652
8753// Minimal "moment" style formatting to get a string
8854// representation of an (older) date relative to now,
@@ -136,19 +102,31 @@ function StatusIcon(
136102 return (
137103 < div
138104 title = { title }
139- style = { `background-color: ${ color } ; width: 20px; height: 20px; border-radius: 20px` }
105+ style = { {
106+ backgroundColor : color ,
107+ width : "20px" ,
108+ borderRadius : "20px" ,
109+ } }
140110 >
141111 </ div >
142112 ) ;
143113}
144114
145- const BGCharmRow = lift ( (
146- { charm, charms } : { charm : BGCharmEntry ; charms : BGCharmEntries } ,
147- ) => {
148- const { integration, createdAt, updatedAt, disabledAt, lastRun, status } =
149- charm ;
150- const space = charm . space . slice ( - 4 ) ;
151- const charmId = charm . charmId . slice ( - 4 ) ;
115+ function getRenderData (
116+ charm : BGCharmEntry ,
117+ ) {
118+ const {
119+ integration,
120+ space : rawSpace ,
121+ charmId : rawCharmId ,
122+ createdAt,
123+ updatedAt,
124+ disabledAt,
125+ lastRun,
126+ status,
127+ } = charm ;
128+ const space = rawSpace . slice ( - 4 ) ;
129+ const charmId = rawCharmId . slice ( - 4 ) ;
152130 const name = `#${ space } /#${ charmId } ` ;
153131
154132 const createdAtDate = new Date ( createdAt ) ;
@@ -164,32 +142,15 @@ Last run ${lastRunDate ? fromNow(lastRunDate) : "never"} ${
164142 lastRunDate ? `(${ lastRunDate . toLocaleString ( ) } )` : ""
165143 } `;
166144
167- return (
168- < div className = "bg-charm-row" >
169- < div className = "toggle-button" >
170- < button
171- onClick = { toggleCharm ( { charm } ) }
172- type = "button"
173- >
174- < StatusIcon status = { status } disabledAt = { disabledAt } > </ StatusIcon >
175- </ button >
176- </ div >
177- < div className = "name ellipsis" title = { details } >
178- { name }
179- < span className = "integration" > { integration } </ span >
180- </ div >
181- < div className = "status ellipsis" > { statusDisplay } </ div >
182- < div className = "delete" >
183- < button
184- onClick = { deleteCharm ( { charm, charms } ) }
185- type = "button"
186- >
187- Delete
188- </ button >
189- </ div >
190- </ div >
191- ) ;
192- } ) ;
145+ return {
146+ status,
147+ statusDisplay,
148+ disabledAt,
149+ details,
150+ integration,
151+ name,
152+ } ;
153+ }
193154
194155const css = `
195156.bg-charm-container {
@@ -233,9 +194,8 @@ const css = `
233194}
234195` ;
235196
236- export default recipe (
237- InputSchema ,
238- ResultSchema ,
197+ export default recipe < InputSchema , ResultSchema > (
198+ "BG Admin" ,
239199 ( { charms } ) => {
240200 derive ( charms , ( charms ) => {
241201 console . log ( "bg charm list:" , charms ) ;
@@ -246,13 +206,42 @@ export default recipe(
246206 < div >
247207 < style > { css } </ style >
248208 < div className = "bg-charm-container" >
249- { charms . map ( ( charm ) => (
250- < BGCharmRow
251- charms = { charms }
252- charm = { charm }
253- >
254- </ BGCharmRow >
255- ) ) }
209+ { charms . map ( ( charm ) => {
210+ const {
211+ status,
212+ disabledAt,
213+ details,
214+ name,
215+ integration,
216+ statusDisplay,
217+ } = getRenderData ( charm ) ;
218+ return (
219+ < div className = "bg-charm-row" >
220+ < div className = "toggle-button" >
221+ < button
222+ onClick = { toggleCharm ( { charm } ) }
223+ type = "button"
224+ >
225+ < StatusIcon status = { status } disabledAt = { disabledAt } >
226+ </ StatusIcon >
227+ </ button >
228+ </ div >
229+ < div className = "name ellipsis" title = { details } >
230+ { name }
231+ < span className = "integration" > { integration } </ span >
232+ </ div >
233+ < div className = "status ellipsis" > { statusDisplay } </ div >
234+ < div className = "delete" >
235+ < button
236+ onClick = { deleteCharm ( { charm, charms } ) }
237+ type = "button"
238+ >
239+ Delete
240+ </ button >
241+ </ div >
242+ </ div >
243+ ) ;
244+ } ) }
256245 </ div >
257246 </ div >
258247 ) ,
0 commit comments