Skip to content

Commit 59a104c

Browse files
committed
Sync with giraffeql-boilerplate fff8663
1 parent 60e83c4 commit 59a104c

File tree

8 files changed

+77
-44
lines changed

8 files changed

+77
-44
lines changed

backend/functions/src/schema/core/helpers/sql.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ function applyWhere(
473473
}
474474
break;
475475
case "in":
476+
case "nin":
476477
if (Array.isArray(whereSubObject.value)) {
477478
// if array is empty, is equivalent of FALSE
478479
if (whereSubObject.value.length < 1) {
@@ -481,43 +482,20 @@ function applyWhere(
481482
"Must provide non-empty array for (n)in operators"
482483
);
483484
} else {
484-
// if trying to do IN (null), adjust accordingly
485-
if (whereSubObject.value.some((ele) => ele === null)) {
486-
whereSubstatement = `(${whereSubstatement} IN (${whereSubObject.value
487-
.filter((ele) => ele !== null)
488-
.map(() => "?")}) OR ${whereSubstatement} IS NULL)`;
489-
} else {
490-
whereSubstatement += ` IN (${whereSubObject.value.map(
491-
() => "?"
492-
)})`;
493-
}
494-
495-
whereSubObject.value
496-
.filter((ele) => ele !== null)
497-
.forEach((ele) => {
498-
bindings.push(ele);
499-
});
500-
}
501-
} else {
502-
throw new Error("Must provide array for in/nin operators");
503-
}
504-
break;
505-
case "nin":
506-
if (Array.isArray(whereSubObject.value)) {
507-
// if array is empty, is equivalent of TRUE
508-
if (whereSubObject.value.length < 1) {
509-
// whereSubstatement = " TRUE";
510-
throw new Error(
511-
"Must provide non-empty array for (n)in operators"
512-
);
513-
} else {
514-
// if trying to do IN (null), adjust accordingly
515-
if (whereSubObject.value.some((ele) => ele === null)) {
516-
whereSubstatement = `(${whereSubstatement} NOT IN (${whereSubObject.value
485+
const operatorPrefix = operator === "nin" ? "NOT " : "";
486+
487+
if (whereSubObject.value.every((ele) => ele === null)) {
488+
// if every element is null, handle specially
489+
whereSubstatement += ` IS ${operatorPrefix}NULL`;
490+
} else if (whereSubObject.value.some((ele) => ele === null)) {
491+
// if trying to do IN (null, otherValue), adjust accordingly
492+
whereSubstatement = `(${whereSubstatement} ${operatorPrefix}IN (${whereSubObject.value
517493
.filter((ele) => ele !== null)
518-
.map(() => "?")}) OR ${whereSubstatement} IS NULL)`;
494+
.map(() => "?")}) ${
495+
operator === "nin" ? "AND" : "OR"
496+
} ${whereSubstatement} IS ${operatorPrefix}NULL)`;
519497
} else {
520-
whereSubstatement += ` NOT IN (${whereSubObject.value.map(
498+
whereSubstatement += ` ${operatorPrefix}IN (${whereSubObject.value.map(
521499
() => "?"
522500
)})`;
523501
}

frontend/components/interface/crud/deleteRecordInterface.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ export default {
7474
variant: 'success',
7575
})
7676
77+
this.recordInfo.deleteOptions.onSuccess &&
78+
this.recordInfo.deleteOptions.onSuccess(this)
79+
7780
this.$emit('handleSubmit', data)
7881
this.$emit('close')
7982
} catch (err) {

frontend/components/page/viewRecordPage.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
<v-toolbar flat color="accent" dense>
3333
<v-icon left>{{ recordInfo.icon || 'mdi-domain' }}</v-icon>
3434
<v-toolbar-title>
35-
{{ recordInfo.title || recordInfo.name }}
35+
{{ recordInfo.name }}
3636
</v-toolbar-title>
3737
<v-spacer></v-spacer>
3838
<v-btn icon @click="reset()">

frontend/mixins/crud.js

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ export default {
124124
// has the recordInfo been changed?
125125
cancelPageOptionsReset: false,
126126

127+
// has reset been called on this tick already?
128+
resetCalledOnTick: false,
129+
127130
records: [],
128131
totalRecords: 0,
129132
endCursor: null,
@@ -391,6 +394,11 @@ export default {
391394
this.handleVisibilityChange,
392395
false
393396
)
397+
398+
// listen for root refresh events
399+
if (this.recordInfo.paginationOptions.eventListener) {
400+
this.$root.$on(`refresh-${this.recordInfo.typename}`, this.refreshCb)
401+
}
394402
},
395403

396404
destroyed() {
@@ -399,12 +407,22 @@ export default {
399407
'visibilitychange',
400408
this.handleVisibilityChange
401409
)
410+
411+
if (this.recordInfo.paginationOptions.eventListener) {
412+
this.$root.$off(`refresh-${this.recordInfo.typename}`, this.refreshCb)
413+
}
402414
},
403415

404416
methods: {
405417
generateTimeAgoString,
406418
getIcon,
407419

420+
refreshCb() {
421+
this.reset({
422+
resetExpanded: false,
423+
})
424+
},
425+
408426
toggleGridMode() {
409427
this.isGrid = !this.isGrid
410428
// only save the state if not child component
@@ -573,12 +591,12 @@ export default {
573591
if (rawFilterObject.value === '__undefined') return total
574592

575593
// parse '__null' to null first
576-
// also parse '__now()' to current date string
594+
// also parse '__now()' to current unix timestamp (seconds)
577595
const value =
578596
rawFilterObject.value === '__null'
579597
? null
580598
: rawFilterObject.value === '__now()'
581-
? generateDateLocaleString(new Date().getTime() / 1000)
599+
? new Date().getTime() / 1000
582600
: rawFilterObject.value
583601

584602
// apply parseValue function, if any
@@ -891,6 +909,17 @@ export default {
891909
showLoader = true,
892910
clearRecords = true,
893911
} = {}) {
912+
// if reset was already called on this tick, stop execution
913+
if (this.resetCalledOnTick) return
914+
915+
// indicate that reset was called on this tick
916+
this.resetCalledOnTick = true
917+
918+
// reset the indicator on the next tick
919+
this.$nextTick(() => {
920+
this.resetCalledOnTick = false
921+
})
922+
894923
if (clearRecords) {
895924
this.records = []
896925
this.totalRecords = null

frontend/mixins/editRecordInterface.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ export default {
308308
variant: 'success',
309309
})
310310

311+
if (this.mode === 'add' || this.mode === 'edit') {
312+
const onSuccess = this.recordInfo[`${this.mode}Options`].onSuccess
313+
onSuccess && onSuccess(this)
314+
}
315+
311316
this.handleSubmitSuccess(data)
312317

313318
// reset inputs

frontend/models/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './base'
22
export * from './public'
3+
export * from './my'
34
export * from './special'

frontend/services/base.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,18 @@ export function generateDateLocaleString(unixTimestamp: number | null) {
3535
export function generateParseDateTimeStringFn(
3636
defaultTo: 'currentTime' | 'startOfDay' | 'endOfDay' = 'startOfDay'
3737
) {
38-
return function parseDateTimeString(val: string): number | null {
38+
return function parseDateTimeString(
39+
val: string | number | null
40+
): number | null {
3941
// if falsey, default to null
4042
if (!val) return null
4143

44+
// if val is a number, it should be a unix timestamp seconds
45+
// number input should only be possible from a special parsed value, like __now()
46+
if (typeof val === 'number') {
47+
return val
48+
}
49+
4250
// if the string resembles timeLanguage, parse it as such
4351
if (val.match(/^(now|time)/)) return parseTimeLanguage(val)
4452

@@ -585,9 +593,9 @@ export function populateInputObject(
585593

586594
// if autocomplete, attempt to translate the inputObject.value based on the options
587595
if (inputObject.inputType === 'autocomplete') {
588-
inputObject.value = inputObject.options.find(
589-
(ele) => ele.id === inputObject.value
590-
)
596+
inputObject.value =
597+
inputObject.options.find((ele) => ele.id === inputObject.value) ??
598+
null
591599
}
592600
})
593601
)
@@ -698,15 +706,15 @@ export function processQuery(
698706

699707
// if field has args, process them
700708
if (currentFieldInfo.args) {
701-
total[currentFieldInfo.args.path + '.__args'] =
709+
total[`${currentFieldInfo.args.path}.__args`] =
702710
currentFieldInfo.args.getArgs(that)
703711
}
704712
}
705713
})
706714

707715
// if main fieldInfo has args, process them
708716
if (fieldInfo.args) {
709-
total[fieldInfo.args.path + '.__args'] =
717+
total[`${fieldInfo.args.path}.__args`] =
710718
fieldInfo.args.getArgs(that)
711719
}
712720

frontend/types/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ export type RecordInfo<T extends keyof MainTypes> = {
8282
// the function to generate the lockedFilters for routeType "my"
8383
myFilterField?: (that: any) => CrudRawFilterObject[]
8484

85+
// should a $root event listener for refreshing be added
86+
eventListener?: boolean
87+
8588
// all of the possible usable filters
8689
filterOptions: `${T}FilterByObject` extends keyof InputTypes
8790
? FilterObject[]
@@ -116,6 +119,8 @@ export type RecordInfo<T extends keyof MainTypes> = {
116119
component?: any
117120
// if not createX, the custom create operation name
118121
operationName?: string
122+
// function that runs when recorded is successfully added
123+
onSuccess?: (that) => void
119124
}
120125

121126
importOptions?: {
@@ -138,6 +143,8 @@ export type RecordInfo<T extends keyof MainTypes> = {
138143
icon?: string
139144
// replacement text
140145
text?: string
146+
// function that runs when recorded is successfully edited
147+
onSuccess?: (that) => void
141148
}
142149

143150
deleteOptions?: {
@@ -146,6 +153,8 @@ export type RecordInfo<T extends keyof MainTypes> = {
146153
component?: any
147154
// if not createX, the custom create operation name
148155
operationName?: string
156+
// function that runs when recorded is successfully deleted
157+
onSuccess?: (that) => void
149158
}
150159

151160
viewOptions?: {

0 commit comments

Comments
 (0)