Vue.component('dashboard-location-selector', {
template: `
`,
props:
{
selectedNetworkOwner: {type: String, required: true},
selectedCampaign: {type: String, required: true},
selectedGemeinde: {type: String, required: true},
}
,
data() {
return {
showNetOwnerFilter: window.TT_CONFIG["IS_ADMIN"] === 'true',
filterOptions: {
netOwners: [],
campaigns: [],
gemeinden: [],
},
};
},
async mounted() {
await this.fetchNetOwnerFilterOptions();
await this.fetchCampaignFilterOptions();
await this.fetchCampaignGemeindeFilterOptions();
},
methods: {
async fetchNetOwnerFilterOptions() {
const response = await axios.get(`${window.TT_CONFIG['BASE_URL']}/getNetOwnerFilterOptions`);
this.filterOptions.netOwners = [{value: 'all', text: 'Alle'}, ...response.data];
},
async fetchCampaignFilterOptions() {
const response = await axios.post(`${window.TT_CONFIG['BASE_URL']}/getCampaignFilterOptions`, {netOwners: this.selectedNetworkOwner === 'all' ? undefined : this.selectedNetworkOwner});
this.filterOptions.campaigns = [{value: 'all', text: 'Alle'}, ...response.data];
},
async fetchCampaignGemeindeFilterOptions() {
const response = await axios.post(`${window.TT_CONFIG['BASE_URL']}/getCampaignGemeindeFilterOptions`,
{netOwners: this.selectedNetworkOwner === 'all' ? undefined : this.selectedNetworkOwner, campaigns: this.selectedCampaign === 'all' ? undefined : this.selectedCampaign});
this.filterOptions.gemeinden = [{value: 'all', text: 'Alle'}, ...response.data];
},
},
watch: {
selectedNetworkOwner() {
this.fetchCampaignFilterOptions();
this.fetchCampaignGemeindeFilterOptions();
},
selectedCampaign() {
this.fetchCampaignGemeindeFilterOptions();
}
},
});
Vue.component('tt-dashboard-display-card', {
props: ['header', 'icon', 'text', 'subHeaders', 'color'],
computed: {
cardHeight() {
return `${96 + 25 * this.subHeaders?.length ?? 0}px`;
}
},
template: `
`
});
Vue.component('tt-timeline-chart', {
props: {
datasets: {
type: Array,
required: true
},
label: {
type: String,
default: ''
},
showGrid: {
type: Boolean,
default: true
}
},
template: '',
data() {
return {
chart: null
};
},
mounted() {
this.renderChart();
},
methods: {
renderChart() {
if (this.chart) {
this.chart.destroy();
}
const ctx = this.$refs.chart.getContext('2d');
this.chart = new Chart(ctx, {
type: 'line',
data: {
labels: this.datasets[0].data.map(item => item.date),
datasets: this.datasets.map((dataset, index) => ({
label: dataset.label,
data: dataset.data.map(item => item.value),
borderColor: dataset.color,
backgroundColor: dataset.fill ? dataset.color : 'transparent',
tension: 0.1,
radius: 5,
hoverRadius: 8,
fill: dataset.fill,
yAxisID: dataset.yAxisID,
order: dataset.order || index
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'week',
displayFormats: {
week: 'DD.MM'
}
},
title: {
display: true,
text: 'Datum'
},
grid: {
display: this.showGrid
},
ticks: {
callback: (value, index, values) => {
const date = moment(value);
return this.label === 'KW' ? `KW ${date.isoWeek()}` : date.format('DD.MM');
}
}
},
y: {
beginAtZero: true,
title: {
display: true,
text: 'Anzahl'
},
grid: {
display: this.showGrid
}
}
},
plugins: {
tooltip: {
callbacks: {
title: (context) => {
const date = moment(context[0].label);
return this.label === 'KW' ? `KW ${date.isoWeek()}` : date.format('DD.MM.YYYY');
}
}
}
}
}
});
}
},
watch: {
datasets: {
deep: true,
handler() {
this.renderChart();
}
}
}
});
Vue.component('dashboard-default', {
props: ['dashboardData'],
data() {
return {
selectedTimeframe: 'all',
window: window,
moment: moment
}
},
methods: {
exportTimeline() {
// we need all timelines with "ONT installiert", "Leerrohr", "Bestellungen"
const data = this.dashboardData.timeline[0].map((item, index) => ({
date: item.date,
bestellungen: item.value,
leerrohr: this.dashboardData.timeline_leerrohr[0][index].value,
ont_installiert: this.dashboardData.timeline_ont_installed[0][index].value
}));
// dont use any library for the csv
const csv = [
['Datum', 'Bestellungen', 'Leerrohr', 'ONT installiert'],
...data.map(item => [item.date, item.bestellungen, item.leerrohr, item.ont_installiert])
].map(row => row.join(';')).join('\n');
const blob = new Blob([csv], {type: 'text/csv'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'timeline.csv';
a.click();
URL.revokeObjectURL(url);
}
},
template: `
Bestellungen
Baufortschritt
Installationsfortschritt
Bestellverlauf
`
})
Vue.component('dashboard-rml', {
props: ['dashboardData'],
data() {
return {
selectedTimeframe: 'all',
window: window,
moment: moment
}
},
methods: {
exportTimeline() {
// we need all timelines with "ONT installiert", "Leerrohr", "Bestellungen"
const data = this.dashboardData.timeline[0].map((item, index) => ({
date: item.date,
bestellungen: item.value,
leerrohr: this.dashboardData.timeline_leerrohr[0][index].value,
ont_installiert: this.dashboardData.timeline_ont_installed[0][index].value
}));
// dont use any library for the csv
const csv = [
['Datum', 'Bestellungen', 'Leerrohr', 'ONT installiert'],
...data.map(item => [item.date, item.bestellungen, item.leerrohr, item.ont_installiert])
].map(row => row.join(';')).join('\n');
const blob = new Blob([csv], {type: 'text/csv'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'timeline.csv';
a.click();
URL.revokeObjectURL(url);
}
},
template: `
Bestellungen
Baufortschritt
Installationsfortschritt Provider
Bestellverlauf
`
})
Vue.component('dashboard-new', {
template: `
Akquise Statistiken
`,
data() {
return {
window,
dashboardData: {
type: 'unloaded',
order_max_home_addrdb: 0,
order_actual_order: "0",
order_efh: "0",
order_efh_vorsorge: "0",
order_efh_vollanschluss: "0",
order_mph: "0",
order_mph_vorsorge: "0",
order_mph_vollanschluss: "0",
baufortschritt_140: "0",
installationspaket_erhalten: "0",
lehrrohr_im_haus: "0",
inhouse_kabel_verlegt_efh: "0",
inhouse_kabel_verlegt_mph: "0",
installationsfortschritt_245: "0",
ont_installiert_300: "0",
vollanschluss_dokumentiert_350: "0",
vorsorge_dokumentiert_351: "0",
provider_bestellt_500: "0",
timeline: []
},
selectedNetworkOwner: 'all',
selectedCampaign: 'all',
selectedGemeinde: 'all',
isLoading: false
};
},
async mounted() {
await this.fetchDashboardData();
},
methods: {
async fetchDashboardData() {
this.isLoading = true;
try {
const response = await axios.post(`${window.TT_CONFIG['BASE_URL']}/getDashboardData`, {
netOwners: this.selectedNetworkOwner === 'all' ? '' : this.selectedNetworkOwner,
campaigns: this.selectedCampaign === 'all' ? '' : this.selectedCampaign,
gemeinden: this.selectedGemeinde === 'all' ? '' : this.selectedGemeinde,
});
console.log(response.data);
this.dashboardData = response.data;
} catch (error) {
console.error('Error fetching dashboard data:', error);
} finally {
this.isLoading = false;
}
},
async csvExport() {
this.isLoading = true;
let objectUrl = null;
try {
const { data } = await axios.get(`${window.TT_CONFIG['BASE_URL']}/export`);
if (!data?.length) return console.warn('No data available to export.');
const headers = Object.keys(data[0]);
const csvContent = [
headers.join(';'),
...data.map(row => headers.map(header => {
const cell = String(row[header] ?? ''); // Ensure value is a string, default to empty
return /[";\n]/.test(cell) ? `"${cell.replace(/"/g, '""')}"` : cell;
}).join(';'))
].join('\n');
const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' });
objectUrl = URL.createObjectURL(blob);
const link = Object.assign(document.createElement('a'), {
href: objectUrl,
download: `Statistik_${new Date().toISOString().slice(0, 10)}.csv`
});
document.body.appendChild(link);
link.click();
link.remove(); // More modern than removeChild(link)
} catch (error) {
console.error('Error exporting CSV:', error);
} finally {
this.isLoading = false;
if (objectUrl) URL.revokeObjectURL(objectUrl);
}
}
},
watch: {
selectedNetworkOwner() {
this.fetchDashboardData();
},
selectedCampaign() {
this.fetchDashboardData();
},
selectedGemeinde() {
this.fetchDashboardData();
}
}
});