<script setup lang="ts"> import {computed, reactive, Ref, ref, watch} from "vue"; import {TableIdentifier, Transforms, useAPI, VirtualViewCreation} from "@/services/api"; import {useMainStore} from "@/services/mainStore"; import {useSelectionStore} from "@/services/selectionStore"; import SimpleJoin from "@/components/subcomponents/transform/SimpleJoin.vue"; import ExtractJson from "@/components/subcomponents/transform/ExtractJson.vue"; import EditColumns from "@/components/subcomponents/transform/EditColumns.vue"; import TableFilter from "@/components/subcomponents/transform/TableFilter.vue"; import TimeFilter from "@/components/subcomponents/transform/TimeFilter.vue"; import TableSelector from "@/components/helpers/TableSelector.vue"; import {storeToRefs} from "pinia"; import InfoDialog from "@/components/helpers/InfoDialog.vue"; import {rules} from "@/services/utils"; import AdvancedRaw from "@/components/subcomponents/transform/AdvancedRaw.vue"; type WidgetKind = "base" | "transform" | "advanced" const modes = [ {title: "Join", value: "base"}, {title: "Transform", value: "transform"}, {title: "Advanced", value: "advanced"}] const widgetComponents: { title: string, value: string, kind: WidgetKind }[] = [ { title: "Advanced Raw", value: "advancedRaw", kind: "advanced" }, { title: "Column Editor", value: "editColumns", kind: "transform" }, { title: "JSON Extractor", value: "extractJson", kind: "transform" }, { title: "Table Filter", value: "tableFilter", kind: "transform" }, { title: "Simple Join", value: "simpleJoin", kind: "base" }, ] const widgetInstances: { [key: string]: { ref: any, isValid: Ref<boolean>, kind: WidgetKind, cls: any } } = { "editColumns": {cls: EditColumns, ref: ref(), isValid: ref<boolean>(false), kind: "transform"}, "extractJson": {cls: ExtractJson, ref: ref(), isValid: ref<boolean>(false), kind: "transform"}, "tableFilter": {cls: TableFilter, ref: ref(), isValid: ref<boolean>(false), kind: "transform"}, "simpleJoin": {cls: SimpleJoin, ref: ref(), isValid: ref<boolean>(false), kind: "base"}, "advancedRaw": {cls: AdvancedRaw, ref: ref(), isValid: ref<boolean>(false), kind: "advanced"}, } const store = useMainStore() const selection = useSelectionStore() const api = useAPI() const {virtualDBSchema} = storeToRefs(store) const {selectedTable} = storeToRefs(selection) const dialog = reactive({content: "", show: false, success: null, title: "Virtual View Creation"}) const loading = ref(false) const mode = ref("transform") const removeBaseTable = ref(false) const permitOverride = ref(true) const viewIdentifier = reactive<TableIdentifier>({source: "virtual", schema: "virtual", name: ""}) const tab = ref("columnEdit") const widgetList = computed(() => { const kind = mode.value return widgetComponents.filter(item => item.kind === kind) }) watch(widgetList, (wl) => tab.value = wl[0].value, {flush: "post"}) const widgetInstance = computed(() => widgetInstances[tab.value]) const widgetComponent = computed(() => widgetInstance.value.cls) const isValid = computed(() => (mode.value !== "transform" || !!selectedTable.value) && !!viewIdentifier.schema && !!viewIdentifier.name && uniqueName()) const disabled = computed(() => { const selfValid = isValid.value const widgetValid = widgetInstance.value?.isValid.value return !selfValid || !widgetValid }) function createRequest(base: Transforms.Base, transforms: Transforms.Transform[]): VirtualViewCreation { return { name: viewIdentifier.name, schema: viewIdentifier.schema, table_creation: base, transforms: transforms } } async function attemptCreation(request: VirtualViewCreation, options?: { dropBase: boolean, base: TableIdentifier }): Promise<boolean> { loading.value = true const r = await api.createVirtualView(request, {override_if_exists: permitOverride.value}) const success = !!r?.data dialog.success = success if (success) { dialog.content = `"${r.data.table_meta.name}" has been created` if (options?.dropBase) { const t = options.base const r2 = await api.dropVirtualView({schema: t.schema, view: t.name}) if (r2) dialog.content += ` and its base ${t.name} has been dropped` } } else { dialog.content = `Virtual view creation has failed` } dialog.show = true loading.value = false return success } async function onSubmit() { const widget = widgetInstance.value if (!widget) return let base = {operation: "existing", base_table: selectedTable.value} as Transforms.ExistingTable let transforms = [] if (widget.kind === "base" || widget.kind === "advanced") base = widget.ref.createBase() if (widget.kind === "transform" || widget.kind === "advanced") transforms = widget.ref.createTransforms() const r = createRequest(base, transforms) attemptCreation(r).then(s => !!s && setTimeout(store.reloadVirtualDB, 500)) } function uniqueName(v) { return permitOverride.value || !Object.values(virtualDBSchema.value.tables).some(tm => tm.schema_name === viewIdentifier.schema && tm.name === viewIdentifier.name) || "view already exists" } function resetWidget() { const widget = widgetInstance.value; if (!!widget) widget.ref.resetAll() } </script> <template> <v-card title="Create Virtual View" class="fill-height" variant="text"> <v-container fluid> <v-row justify="start" align="start" class="d-flex"> <v-col cols="2"> <v-select label="Mode" :items="modes" v-model="mode" hide-details variant="solo"></v-select> </v-col> <v-col cols="3"> <v-text-field label="Schema Name" :rules="[rules.required, uniqueName]" prepend-icon="mdi-folder-pound-outline" v-model="viewIdentifier.schema" validate-on="invalid-input eager" clearable></v-text-field> </v-col> <v-col cols="4"> <v-text-field label="View Name" :rules="[rules.required, uniqueName]" prepend-icon="mdi-table" v-model="viewIdentifier.name" validate-on="invalid-input eager" clearable></v-text-field> </v-col> </v-row> <template v-if="mode === 'transform'"> <v-row justify="start" align="center" class="text-center"> <v-col cols="4"> <TableSelector v-model:selected-table="selectedTable" source-d-b="either" :props="{'hide-details': true}"></TableSelector> </v-col> <v-col cols="4"> <v-checkbox label="remove base table afterwards?" density="compact" v-model="removeBaseTable" hide-details></v-checkbox> </v-col> <v-col cols="4"> <v-checkbox label="permit overriding existing table" density="compact" v-model="permitOverride" hide-details></v-checkbox> </v-col> </v-row> </template> </v-container> <v-card class="pa-2" variant="outlined" min-height="200"> <v-card-text> <v-tabs v-model="tab" align-tabs="start"> <v-tab v-for="widget in widgetList" :text="widget.title" :value="widget.value" :key="widget.value"></v-tab> </v-tabs> <v-tabs-window v-model="tab" class="fill-height"> <v-tabs-window-item v-for="(item, key) in widgetInstances" :value="key" :key="key"> <component class="mx-auto" :is="item.cls" v-model="item.isValid.value" :ref="el => item.ref = el" :table="selectedTable"></component> </v-tabs-window-item> </v-tabs-window> <v-divider></v-divider> </v-card-text> <v-card-actions> <v-spacer></v-spacer> <v-btn @click="resetWidget" type="reset">Reset</v-btn> <v-btn :disabled="disabled" type="submit" @click="onSubmit">Submit</v-btn> </v-card-actions> </v-card> <InfoDialog v-model:dialog="dialog"></InfoDialog> </v-card> </template> <style scoped> </style>