feat: dashboard table chart download as csv file (#2428)

- download table chart as a csv file
This commit is contained in:
ktx-abhay
2024-01-10 15:07:32 +05:30
committed by GitHub
parent 4422b61a96
commit 36d4aee73a
3 changed files with 123 additions and 12 deletions
@@ -17,8 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<template>
<div
class="panelcontainer"
@mouseover="() => (showFullScreenBtn = true)"
@mouseleave="() => (showFullScreenBtn = false)"
@mouseover="() => (isCurrentlyHoveredPanel = true)"
@mouseleave="() => (isCurrentlyHoveredPanel = false)"
data-test="dashboard-panel-container"
>
<div class="drag-allow">
<q-bar
@@ -28,23 +29,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
style="border-top-left-radius: 3px; border-top-right-radius: 3px"
data-test="dashboard-panel-bar"
>
<q-icon v-if="!viewOnly" name="drag_indicator" data-test="dashboard-panel-drag"/>
<q-icon
v-if="!viewOnly"
name="drag_indicator"
data-test="dashboard-panel-drag"
/>
<div :title="props.data.title" class="panelHeader">
{{ props.data.title }}
</div>
<q-space />
<q-icon
v-if="!viewOnly && showFullScreenBtn && props.data.description != ''"
v-if="
!viewOnly && isCurrentlyHoveredPanel && props.data.description != ''
"
name="info_outline"
style="cursor: pointer"
data-test="dashboard-panel-description-info"
>
<q-tooltip anchor="bottom right" self="top right" max-width="220px">
<div style="white-space: pre-wrap;">{{ props.data.description }}</div>
<div style="white-space: pre-wrap">
{{ props.data.description }}
</div>
</q-tooltip>
</q-icon>
<q-btn
v-if="!viewOnly && showFullScreenBtn"
v-if="!viewOnly && isCurrentlyHoveredPanel"
icon="fullscreen"
flat
size="sm"
@@ -53,6 +62,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
title="Full screen"
data-test="dashboard-panel-fullscreen-btn"
/>
<!-- if table chart then download button as a csv file -->
<q-btn
v-if="
!viewOnly && isCurrentlyHoveredPanel && props.data.type == 'table'
"
icon="download"
flat
size="sm"
padding="1px"
@click="
PanleSchemaRendererRef?.tableRendererRef?.downloadTableAsCSV(
props.data.title
)
"
title="Download as a CSV"
data-test="dashboard-panel-table-download-as-csv-btn"
/>
<q-btn
v-if="dependentAdHocVariable"
:icon="outlinedWarning"
@@ -138,6 +164,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:variablesData="props.variablesData"
@metadata-update="metaDataValue"
@updated:data-zoom="$emit('updated:data-zoom', $event)"
ref="PanleSchemaRendererRef"
></PanelSchemaRenderer>
<q-dialog v-model="showViewPanel">
<QueryInspector :metaData="metaData" :data="props.data"></QueryInspector>
@@ -195,6 +222,9 @@ export default defineComponent({
};
const showText = ref(false);
// need PanleSchemaRendererRef for table download as a csv
const PanleSchemaRendererRef: any = ref(null);
//check if dependent adhoc variable exists
const dependentAdHocVariable = computed(() => {
if (!metaData.value) return false;
@@ -215,7 +245,7 @@ export default defineComponent({
});
// for full screen button
const showFullScreenBtn: any = ref(false);
const isCurrentlyHoveredPanel: any = ref(false);
//for edit panel
const onEditPanel = (data: any) => {
@@ -291,7 +321,7 @@ export default defineComponent({
onEditPanel,
onDuplicatePanel,
deletePanelDialog,
showFullScreenBtn,
isCurrentlyHoveredPanel,
outlinedWarning,
store,
metaDataValue,
@@ -299,7 +329,8 @@ export default defineComponent({
showViewPanel,
dependentAdHocVariable,
confirmDeletePanelDialog,
showText
showText,
PanleSchemaRendererRef,
};
},
methods: {
@@ -19,11 +19,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<div v-show="!errorDetail" style="height: 100%; width: 100%">
<GeoMapRenderer
v-if="panelSchema.type == 'geomap'"
:data="panelData.chartType == 'geomap' ? panelData : { options: {backgroundColor: 'transparent'} }"
:data="
panelData.chartType == 'geomap'
? panelData
: { options: { backgroundColor: 'transparent' } }
"
/>
<TableRenderer
v-else-if="panelSchema.type == 'table'"
:data="panelData.chartType == 'table' ? panelData : { options: {backgroundColor: 'transparent'} }"
:data="
panelData.chartType == 'table'
? panelData
: { options: { backgroundColor: 'transparent' } }
"
ref="tableRendererRef"
/>
<ChartRenderer
v-else
@@ -34,7 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
panelData.chartType != 'geomap' &&
panelData.chartType != 'table')
? panelData
: { options: {backgroundColor: 'transparent'} }
: { options: { backgroundColor: 'transparent' } }
"
@updated:data-zoom="$emit('updated:data-zoom', $event)"
/>
@@ -115,6 +124,9 @@ export default defineComponent({
chartPanelRef
);
// need tableRendererRef to access downloadTableAsCSV method
const tableRendererRef = ref(null);
// hovered series state
// used to show tooltip axis for all charts
const hoveredSeriesState: any = inject("hoveredSeriesState", null);
@@ -253,6 +265,7 @@ export default defineComponent({
panelData,
noData,
metadata,
tableRendererRef,
};
},
});
@@ -25,11 +25,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:rows="data.rows || []"
:columns="data.columns"
row-key="id"
ref="tableRef"
data-test="dashboard-panel-table"
>
</q-table>
</template>
<script lang="ts">
import { exportFile, useQuasar } from "quasar";
import { defineComponent, ref, onMounted, watch } from "vue";
export default defineComponent({
@@ -42,10 +45,74 @@ export default defineComponent({
},
},
setup(props: any) {
const $q = useQuasar();
const tableRef: any = ref(null);
function wrapCsvValue(val: any, formatFn?: any, row?: any) {
let formatted = formatFn !== void 0 ? formatFn(val, row) : val;
formatted =
formatted === void 0 || formatted === null ? "" : String(formatted);
formatted = formatted.split('"').join('""');
/**
* Excel accepts \n and \r in strings, but some other CSV parsers do not
* Uncomment the next two lines to escape new lines
*/
// .split('\n').join('\\n')
// .split('\r').join('\\r')
return `"${formatted}"`;
}
const downloadTableAsCSV = (title?: any) => {
// naive encoding to csv format
const content = [
props?.data?.columns?.map((col: any) => wrapCsvValue(col.label)),
]
.concat(
tableRef?.value?.filteredSortedRows?.map((row: any) =>
props?.data?.columns
?.map((col: any) =>
wrapCsvValue(
typeof col.field === "function"
? col.field(row)
: row[col.field === void 0 ? col.name : col.field],
col.format,
row
)
)
.join(",")
)
)
.join("\r\n");
const status = exportFile(
(title ?? "table-export") + ".csv",
content,
"text/csv"
);
if (status === true) {
$q.notify({
message: "Table downloaded as a CSV file",
color: "positive",
timeout: 2000,
});
} else {
$q.notify({
message: "Browser denied file download...",
color: "negative",
icon: "warning",
});
}
};
return {
pagination: ref({
rowsPerPage: 0,
}),
downloadTableAsCSV,
tableRef,
};
},
});