Added filter and moved sample data to API

This commit is contained in:
Peter Morton 2023-06-29 21:47:18 -05:00
parent 35364ee25e
commit 89fe29c162

View File

@ -1,349 +1,185 @@
<script setup>
import { ref, onMounted } from "vue";
import * as d3 from "d3";
import { sankey, sankeyLinkHorizontal, sankeyLeft } from "d3-sankey";
import { onMounted } from "vue";
import { v4 as uuidv4 } from "uuid";
const data = {
nodes: [
{
name: "Email",
category: "Channel",
},
{
name: "Messaging",
category: "Channel",
},
{
name: "Legacy Live Chat",
category: "Channel",
},
{
name: "Live Chat",
category: "Sub Channel",
},
{
name: "Facebook Messenger",
category: "Sub Channel",
},
{
name: "Twitter DM",
category: "Sub Channel",
},
{
name: "WhatsApp",
category: "Sub Channel",
},
{
name: "Other",
category: "Sub Channel",
},
{
name: "Default",
category: "Queue",
},
{
name: "General Enquires",
category: "Queue",
},
{
name: "Complaints",
category: "Queue",
},
{
name: "Case Closed",
category: "Outcome",
},
{
name: "Case Updated",
category: "Outcome",
},
{
name: "Completed",
category: "Outcome",
},
{
name: "Escalated to Manager",
category: "Outcome",
},
{
name: "No need for response",
category: "Outcome",
},
],
links: [
{
source: "Email",
target: "Default",
value: 342,
},
{
source: "Messaging",
target: "Live Chat",
value: 232,
},
{
source: "Messaging",
target: "Facebook Messenger",
value: 623,
},
{
source: "Messaging",
target: "Twitter DM",
value: 434,
},
{
source: "Messaging",
target: "WhatsApp",
value: 1243,
},
{
source: "Messaging",
target: "Other",
value: 150,
},
{
source: "Live Chat",
target: "Default",
value: 132,
},
{
source: "Live Chat",
target: "Complaints",
value: 90,
},
{
source: "Live Chat",
target: "General Enquires",
value: 42,
},
{
source: "WhatsApp",
target: "Default",
value: 343,
},
{
source: "WhatsApp",
target: "Complaints",
value: 300,
},
{
source: "WhatsApp",
target: "General Enquires",
value: 523,
},
{
source: "Facebook Messenger",
target: "Default",
value: 143,
},
{
source: "Facebook Messenger",
target: "Complaints",
value: 200,
},
{
source: "Facebook Messenger",
target: "General Enquires",
value: 323,
},
{
source: "Twitter DM",
target: "Default",
value: 143,
},
{
source: "Twitter DM",
target: "Complaints",
value: 50,
},
{
source: "Twitter DM",
target: "General Enquires",
value: 223,
},
{
source: "General Enquires",
target: "Case Closed",
value: 421,
},
{
source: "General Enquires",
target: "Completed",
value: 612,
},
{
source: "General Enquires",
target: "Escalated to Manager",
value: 23,
},
{
source: "General Enquires",
target: "No need for response",
value: 241,
},
{
source: "Complaints",
target: "Case Closed",
value: 21,
},
{
source: "Complaints",
target: "Completed",
value: 12,
},
{
source: "Complaints",
target: "Escalated to Manager",
value: 3,
},
{
source: "Complaints",
target: "No need for response",
value: 41,
},
{
source: "Default",
target: "Completed",
value: 41,
},
{
source: "Default",
target: "Case Updated",
value: 410,
},
{
source: "General Enquires",
target: "Case Updated",
value: 50,
},
{
source: "Complaints",
target: "Case Updated",
value: 410,
},
],
};
const multiSelected = ref(['Channel', 'Sub-Channel', 'Queue', 'Outcome'])
const errorMessage = ref(null);
onMounted(() => {
// Specify the dimensions of the chart.
const width = 928;
const height = 600;
const format = d3.format(",.0f");
const linkColor = "source-target"; //
// ["static", "#aaa"],
// ["source-target", "source-target"],
// ["source", "source"],
// ["target", "target"]
// Create a SVG container.
const svg = d3
.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
// Constructs and configures a Sankey generator.
const sankeyGen = sankey()
.nodeId((d) => d.name)
.nodeAlign(sankeyLeft) // d3.sankeyLeft, etc.
.nodeWidth(15)
.nodePadding(10)
.extent([
[1, 5],
[width - 1, height - 5],
]);
// Applies it to the data. We make a copy of the nodes and links objects
// so as to avoid mutating the original.
const { nodes, links } = sankeyGen({
nodes: data.nodes.map((d) => Object.assign({}, d)),
links: data.links.map((d) => Object.assign({}, d)),
});
// Defines a color scale.
const color = d3.scaleOrdinal(d3.schemeCategory10);
// Creates the rects that represent the nodes.
const rect = svg
.append("g")
.attr("stroke", "#000")
.selectAll()
.data(nodes)
.join("rect")
.attr("x", (d) => d.x0)
.attr("y", (d) => d.y0)
.attr("height", (d) => d.y1 - d.y0)
.attr("width", (d) => d.x1 - d.x0)
.attr("fill", (d) => color(d.category));
// Adds a title on the nodes.
rect.append("title").text((d) => `${d.name}\n${format(d.value)} TWh`);
// Creates the paths that represent the links.
const link = svg
.append("g")
.attr("fill", "none")
.attr("stroke-opacity", 0.5)
.selectAll()
.data(links)
.join("g")
.style("mix-blend-mode", "multiply");
// Creates a gradient, if necessary, for the source-target color option.
if (linkColor === "source-target") {
const gradient = link
.append("linearGradient")
.attr("id", (d) => (d.uid = uuidv4()))
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", (d) => d.source.x1)
.attr("x2", (d) => d.target.x0);
gradient
.append("stop")
.attr("offset", "0%")
.attr("stop-color", (d) => color(d.source.category));
gradient
.append("stop")
.attr("offset", "100%")
.attr("stop-color", (d) => color(d.target.category));
}
link
.append("path")
.attr("d", sankeyLinkHorizontal())
.attr(
"stroke",
linkColor === "source-target"
? (d) => `url(#${d.uid})`
: linkColor === "source"
? (d) => color(d.source.category)
: linkColor === "target"
? (d) => color(d.target.category)
: linkColor
)
.attr("stroke-width", (d) => Math.max(1, d.width));
link
.append("title")
.text((d) => `${d.source.name}${d.target.name}\n${format(d.value)} TWh`);
// Adds labels on the nodes.
svg
.append("g")
.selectAll()
.data(nodes)
.join("text")
.attr("x", (d) => (d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6))
.attr("y", (d) => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("text-anchor", (d) => (d.x0 < width / 2 ? "start" : "end"))
.text((d) => d.name);
fetchData()
});
function fetchData() {
//clear errors
errorMessage.value = null;
fetch(
`${import.meta.env.VITE_EO_SERVICES_URL}/interactions-flow?filter=${multiSelected.value}`,
{
credentials: "include", // fetch won't send cookies unless you set credentials
}
)
.then((response) => {
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = response.data || response.statusText;
return Promise.reject(error);
}
response.json().then((data) => {
generateSankey(data);
});
})
.catch((error) => {
console.log(error);
errorMessage.value = error;
});
}
function generateSankey(data) {
// filter data
// const filteredData
if (data != null) {
// Specify the dimensions of the chart.
const width = 928;
const height = 600;
const format = d3.format(",.0f");
const linkColor = "source-target"; //
// ["static", "#aaa"],
// ["source-target", "source-target"],
// ["source", "source"],
// ["target", "target"]
// clear the old svg
d3.select("#chart").select("svg").remove()
// Create a SVG container.
const svg = d3
.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
// Constructs and configures a Sankey generator.
const sankeyGen = sankey()
.nodeId((d) => d.name)
.nodeAlign(sankeyLeft) // d3.sankeyLeft, etc.
.nodeWidth(15)
.nodePadding(10)
.extent([
[1, 5],
[width - 1, height - 5],
]);
// Applies it to the data. We make a copy of the nodes and links objects
// so as to avoid mutating the original.
const { nodes, links } = sankeyGen({
nodes: data.nodes.map((d) => Object.assign({}, d)),
links: data.links.map((d) => Object.assign({}, d)),
});
// Defines a color scale.
const color = d3.scaleOrdinal(d3.schemeCategory10);
// Creates the rects that represent the nodes.
const rect = svg
.append("g")
.attr("stroke", "#000")
.selectAll()
.data(nodes)
.join("rect")
.attr("x", (d) => d.x0)
.attr("y", (d) => d.y0)
.attr("height", (d) => d.y1 - d.y0)
.attr("width", (d) => d.x1 - d.x0)
.attr("fill", (d) => color(d.category));
// Adds a title on the nodes.
rect.append("title").text((d) => `${d.name}\n${format(d.value)} TWh`);
// Creates the paths that represent the links.
const link = svg
.append("g")
.attr("fill", "none")
.attr("stroke-opacity", 0.5)
.selectAll()
.data(links)
.join("g")
.style("mix-blend-mode", "multiply");
// Creates a gradient, if necessary, for the source-target color option.
if (linkColor === "source-target") {
const gradient = link
.append("linearGradient")
.attr("id", (d) => (d.uid = uuidv4()))
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", (d) => d.source.x1)
.attr("x2", (d) => d.target.x0);
gradient
.append("stop")
.attr("offset", "0%")
.attr("stop-color", (d) => color(d.source.category));
gradient
.append("stop")
.attr("offset", "100%")
.attr("stop-color", (d) => color(d.target.category));
}
link
.append("path")
.attr("d", sankeyLinkHorizontal())
.attr(
"stroke",
linkColor === "source-target"
? (d) => `url(#${d.uid})`
: linkColor === "source"
? (d) => color(d.source.category)
: linkColor === "target"
? (d) => color(d.target.category)
: linkColor
)
.attr("stroke-width", (d) => Math.max(1, d.width));
link
.append("title")
.text((d) => `${d.source.name}${d.target.name}\n${format(d.value)} TWh`);
// Adds labels on the nodes.
svg
.append("g")
.selectAll()
.data(nodes)
.join("text")
.attr("x", (d) => (d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6))
.attr("y", (d) => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("text-anchor", (d) => (d.x0 < width / 2 ? "start" : "end"))
.text((d) => d.name);
}
}
</script>
<template>
<div><select v-model="multiSelected" multiple style="width:200px; height:90px">
<option disabled value="">Please select nodes</option>
<option>Channel</option>
<option>Sub-Channel</option>
<option>Queue</option>
<option>Outcome</option>
</select>
<button @click="fetchData">Search Contacts</button>
</div>
<div id="chart"></div>
</template>
<style scoped>