Added new authentication methods using authKey
and cookies. Also now handles changes to base url for use with reverse proxies
This commit is contained in:
134
src/App.vue
134
src/App.vue
@@ -1,102 +1,18 @@
|
||||
<script>
|
||||
import { h, markRaw } from "vue";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { SidebarMenu } from "vue-sidebar-menu";
|
||||
import "vue-sidebar-menu/dist/vue-sidebar-menu.css";
|
||||
|
||||
const separator = {
|
||||
template: '<hr style="border-color: rgba(0, 0, 0, 0.1); margin: 20px;">',
|
||||
}
|
||||
|
||||
const faIcon = (props) => {
|
||||
return {
|
||||
element: markRaw({
|
||||
render: () => h("div", [h(FontAwesomeIcon, { size: "lg", ...props })]),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
SidebarMenu,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menu: [
|
||||
{
|
||||
header: "Services",
|
||||
hiddenOnCollapse: true,
|
||||
},
|
||||
{
|
||||
href: "/",
|
||||
title: "Home",
|
||||
icon: faIcon({ icon: "fa-solid fa-house" }),
|
||||
},
|
||||
{
|
||||
href: "/about",
|
||||
title: "About",
|
||||
icon: faIcon({ icon: "fa-solid fa-question" }),
|
||||
},
|
||||
{
|
||||
component: markRaw(separator),
|
||||
},
|
||||
{
|
||||
href: "/referenceId",
|
||||
title: "Contacts Lookup",
|
||||
icon: faIcon({ icon: "fa-solid fa-rectangle-list" }),
|
||||
},
|
||||
{
|
||||
href: "/interactionsFlow",
|
||||
title: "Interactions Flow",
|
||||
icon: faIcon({ icon: "fa-solid fa-route" }),
|
||||
},
|
||||
{
|
||||
href: "/customerAccount/12345",
|
||||
title: "Customer Account Example",
|
||||
icon: faIcon({ icon: "fa-solid fa-id-card" }),
|
||||
},
|
||||
],
|
||||
collapsed: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.onResize();
|
||||
window.addEventListener("resize", this.onResize);
|
||||
},
|
||||
methods: {
|
||||
onResize() {
|
||||
if (window.innerWidth <= 767) {
|
||||
this.isOnMobile = true;
|
||||
this.collapsed = true;
|
||||
} else {
|
||||
this.isOnMobile = false;
|
||||
this.collapsed = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<sidebar-menu
|
||||
v-model:collapsed="collapsed"
|
||||
:menu="menu"
|
||||
:show-one-child="true"
|
||||
/>
|
||||
<div id="services" :class="[{ collapsed: collapsed }]">
|
||||
<div class="services">
|
||||
<div class="container">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
<div class="flex-container">
|
||||
|
||||
<router-view class="view sidebar" name="SideBarView"></router-view>
|
||||
|
||||
<div class="flex-child">
|
||||
<router-view class="view main-content"></router-view>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- <div>
|
||||
<router-link to="/">Home</router-link>
|
||||
<router-link to="/about">About</router-link>
|
||||
<router-link to="/referenceId">Reference ID Tracker</router-link>
|
||||
<router-link to="/interactionsFlow">Interactions Flow</router-link>
|
||||
<router-link to="/customerAccount">Customer Account</router-link>
|
||||
</div> -->
|
||||
</template>
|
||||
<style lang="scss">
|
||||
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600");
|
||||
@@ -114,35 +30,17 @@ body {
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
#services {
|
||||
padding-left: 290px;
|
||||
transition: 0.3s ease;
|
||||
}
|
||||
|
||||
#services.collapsed {
|
||||
padding-left: 65px;
|
||||
}
|
||||
|
||||
#services.onmobile {
|
||||
padding-left: 65px;
|
||||
}
|
||||
|
||||
.sidebar-overlay {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
.flex-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #000;
|
||||
opacity: 0.5;
|
||||
z-index: 900;
|
||||
}
|
||||
|
||||
.services {
|
||||
padding: 50px;
|
||||
.flex-child {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
border: 2px solid yellow;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
}
|
||||
</style>
|
||||
// .flex-child:first-child {
|
||||
// margin-right: 20px;
|
||||
// } </style>
|
||||
|
||||
@@ -1,44 +1,57 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import HomeView from "../views/HomeView.vue";
|
||||
import ReferenSearchByReferenceViewceID from "../views/SearchByReferenceView.vue";
|
||||
import AboutView from "../views/AboutView.vue";
|
||||
import DebugFrameView from "../views/DebugFrameView.vue";
|
||||
import SideBarView from "../views/SideBarView.vue";
|
||||
import SearchByReferenceView from "../views/SearchByReferenceView.vue";
|
||||
import InteractionsFlowView from "../views/InteractionsFlowView.vue";
|
||||
import CustomerAccountView from "../views/CustomerAccountView.vue";
|
||||
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
name: "home",
|
||||
component: HomeView,
|
||||
components: { default: HomeView, SideBarView: SideBarView },
|
||||
props: { default: false, SideBarView: true }
|
||||
},
|
||||
{
|
||||
path: "/about",
|
||||
name: "about",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
|
||||
components: { default: AboutView, SideBarView: SideBarView },
|
||||
props: { default: false, SideBarView: true }
|
||||
},
|
||||
{
|
||||
path: "/referenceId",
|
||||
name: "referenceId",
|
||||
component: ReferenSearchByReferenceViewceID,
|
||||
components: { default: SearchByReferenceView, SideBarView: SideBarView },
|
||||
props: { default: route => ({ sessionIdentifier: route.query._sessionIdentifier }) }
|
||||
},
|
||||
{
|
||||
path: "/interactionsFlow",
|
||||
name: "interactionsFlow",
|
||||
component: InteractionsFlowView,
|
||||
components: { default: InteractionsFlowView, SideBarView: SideBarView },
|
||||
props: { default: route => ({ sessionIdentifier: route.query._sessionIdentifier }) }
|
||||
},
|
||||
{
|
||||
path: "/customerAccount/:accountNumber",
|
||||
name: "customerAccount",
|
||||
component: CustomerAccountView,
|
||||
props: true,
|
||||
components: { default: CustomerAccountView, SideBarView: SideBarView },
|
||||
props: { default: true, SideBarView: true }
|
||||
},
|
||||
{
|
||||
path: "/debug",
|
||||
name: "debug",
|
||||
component: DebugFrameView,
|
||||
components: { default: DebugFrameView },
|
||||
props: { default: true }
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
base: `${import.meta.env.VITE_ROUTER_BASE}`,
|
||||
routes,
|
||||
});
|
||||
|
||||
|
||||
23
src/views/DebugFrameView.vue
Normal file
23
src/views/DebugFrameView.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import { useCookies } from "vue3-cookies";
|
||||
const { cookies } = useCookies();
|
||||
|
||||
export default {
|
||||
name: "DebugFrameView",
|
||||
|
||||
data() {
|
||||
|
||||
return {
|
||||
route : JSON.stringify(this.$route, null, 2),
|
||||
cookies : JSON.stringify(cookies.keys(), null, 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<h1>Adaptive Frame Debug</h1>
|
||||
<h2>Route information</h2>
|
||||
<div>{{ route }}</div>
|
||||
<h2>Cookie information</h2>
|
||||
<div>{{ cookies }}</div>
|
||||
</template>
|
||||
@@ -11,8 +11,5 @@
|
||||
>
|
||||
Integration.
|
||||
</p>
|
||||
<h2>Services Available</h2>
|
||||
<h3><a href="/referenceId">Reference ID</a></h3>
|
||||
<p>/referenceId</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,12 @@ import * as d3 from "d3";
|
||||
import * as align from "d3-sankey";
|
||||
import { sankey, sankeyLinkHorizontal } from "d3-sankey";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import ErrorMessage from "../components/ErrorMessage.vue";
|
||||
|
||||
const props = defineProps({
|
||||
username: { type: String, default: "" },
|
||||
sessionIdentifier: { type: String, default: "" }
|
||||
});
|
||||
|
||||
const filterOptions = ["Channel", "SubChannel", "Queue", "Outcome"];
|
||||
const multiSelected = ref(filterOptions);
|
||||
@@ -22,29 +28,39 @@ function fetchData() {
|
||||
//clear errors
|
||||
errorMessage.value = null;
|
||||
|
||||
fetch(
|
||||
`${import.meta.env.VITE_EO_SERVICES_URL}/api/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);
|
||||
var authKey;
|
||||
|
||||
if (props.sessionIdentifier.length > 0) {
|
||||
authKey = props.sessionIdentifier;
|
||||
} else if (props.username.length > 0) {
|
||||
authKey = props.username;
|
||||
} else {
|
||||
errorMessage.value = "_sessionIdentifier or username must be passed as query params."
|
||||
}
|
||||
if (authKey) {
|
||||
fetch(
|
||||
`${import.meta.env.VITE_EO_SERVICES_URL}/api/interactions-flow?filter=${multiSelected.value
|
||||
}&authKey=${authKey}`,
|
||||
{
|
||||
credentials: "include", // fetch won't send cookies unless you set credentials
|
||||
}
|
||||
response.json().then((data) => {
|
||||
generateSankey(data);
|
||||
)
|
||||
.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;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
errorMessage.value = error;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function generateSankey(data) {
|
||||
@@ -80,9 +96,9 @@ function generateSankey(data) {
|
||||
.nodeId((d) => d.name)
|
||||
.nodeAlign(
|
||||
align[
|
||||
`sankey${alignSelected.value[0].toUpperCase()}${alignSelected.value.slice(
|
||||
1
|
||||
)}`
|
||||
`sankey${alignSelected.value[0].toUpperCase()}${alignSelected.value.slice(
|
||||
1
|
||||
)}`
|
||||
]
|
||||
) // d3.sankeyLeft, etc.
|
||||
.nodeWidth(15)
|
||||
@@ -154,10 +170,10 @@ function generateSankey(data) {
|
||||
linkColor === "source-target"
|
||||
? (d) => `url(#${d.uid})`
|
||||
: linkColor === "source"
|
||||
? (d) => color(d.source.category)
|
||||
: linkColor === "target"
|
||||
? (d) => color(d.target.category)
|
||||
: linkColor
|
||||
? (d) => color(d.source.category)
|
||||
: linkColor === "target"
|
||||
? (d) => color(d.target.category)
|
||||
: linkColor
|
||||
)
|
||||
.attr("stroke-width", (d) => Math.max(1, d.width));
|
||||
|
||||
@@ -194,6 +210,7 @@ function generateSankey(data) {
|
||||
</div>
|
||||
|
||||
<div id="chart"></div>
|
||||
<ErrorMessage v-if="errorMessage" :message="errorMessage" />
|
||||
</template>
|
||||
<style scoped>
|
||||
svg {
|
||||
|
||||
@@ -5,6 +5,11 @@ import ContactTable from "../components/ContactTable.vue";
|
||||
import ContactsSummary from "../components/ContactsSummary.vue";
|
||||
import ErrorMessage from "../components/ErrorMessage.vue";
|
||||
|
||||
const props = defineProps({
|
||||
username: { type: String, default: "" },
|
||||
sessionIdentifier: { type: String, default: "" }
|
||||
});
|
||||
|
||||
const referenceId = ref("");
|
||||
const contactData = ref(null);
|
||||
const errorMessage = ref(null);
|
||||
@@ -17,29 +22,40 @@ function fetchData() {
|
||||
//clear errors
|
||||
errorMessage.value = null;
|
||||
contactData.value = null;
|
||||
fetch(
|
||||
`${
|
||||
import.meta.env.VITE_EO_SERVICES_URL
|
||||
}/api/unified-data-gateway?referenceId=${referenceId.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);
|
||||
|
||||
var authKey;
|
||||
|
||||
if (props.sessionIdentifier.length > 0) {
|
||||
authKey = props.sessionIdentifier;
|
||||
} else if (props.username.length > 0) {
|
||||
authKey = props.username;
|
||||
} else {
|
||||
errorMessage.value = "_sessionIdentifier or username must be passed as query params."
|
||||
}
|
||||
if (authKey) {
|
||||
fetch(
|
||||
`${import.meta.env.VITE_EO_SERVICES_URL
|
||||
}/api/unified-data-gateway?referenceId=${referenceId.value}&authKey=${authKey}`,
|
||||
{
|
||||
credentials: "include", // fetch won't send cookies unless you set credentials
|
||||
}
|
||||
response.json().then((data) => {
|
||||
contactData.value = data;
|
||||
)
|
||||
.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) => {
|
||||
contactData.value = data;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
errorMessage.value = error;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
errorMessage.value = error;
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@@ -47,22 +63,14 @@ function fetchData() {
|
||||
<h2>Search Criteria</h2>
|
||||
<div>
|
||||
<label for="referenceId">Reference ID: </label>
|
||||
<input
|
||||
id="referenceId"
|
||||
:value="referenceId"
|
||||
placeholder="enter Reference ID here"
|
||||
@input="onInput"
|
||||
/>
|
||||
<input id="referenceId" :value="referenceId" placeholder="enter Reference ID here" @input="onInput" />
|
||||
<button @click="fetchData">Search Contacts</button>
|
||||
</div>
|
||||
|
||||
<h2>Results</h2>
|
||||
<ContactsSummary v-if="contactData" :summary="contactData.data.summary" />
|
||||
<div v-else>No Contacts Found</div>
|
||||
<ContactTable
|
||||
v-if="contactData"
|
||||
:table-data="contactData.data.findContactsCompletedBetween.edges"
|
||||
/>
|
||||
<ContactTable v-if="contactData" :table-data="contactData.data.findContactsCompletedBetween.edges" />
|
||||
<ErrorMessage v-if="errorMessage" :message="errorMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
95
src/views/SideBarView.vue
Normal file
95
src/views/SideBarView.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<script>
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { SidebarMenu } from "vue-sidebar-menu";
|
||||
import "vue-sidebar-menu/dist/vue-sidebar-menu.css";
|
||||
import { h, markRaw } from "vue";
|
||||
|
||||
const separator = {
|
||||
template: '<hr style="border-color: rgba(0, 0, 0, 0.1); margin: 20px;">',
|
||||
}
|
||||
|
||||
const faIcon = (props) => {
|
||||
return {
|
||||
element: markRaw({
|
||||
render: () => h("div", [h(FontAwesomeIcon, { size: "lg", ...props })]),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "SideBarView",
|
||||
components: {
|
||||
SidebarMenu,
|
||||
},
|
||||
|
||||
// props: {
|
||||
// sidebar: {
|
||||
// type: Boolean,
|
||||
// required: false
|
||||
// }
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
menu: [
|
||||
{
|
||||
header: "Services",
|
||||
hiddenOnCollapse: true,
|
||||
},
|
||||
{
|
||||
href: "/",
|
||||
title: "Home",
|
||||
icon: faIcon({ icon: "fa-solid fa-house" }),
|
||||
},
|
||||
{
|
||||
href: "/about",
|
||||
title: "About",
|
||||
icon: faIcon({ icon: "fa-solid fa-question" }),
|
||||
},
|
||||
{
|
||||
component: markRaw(separator),
|
||||
},
|
||||
{
|
||||
href: "/referenceId",
|
||||
title: "Contacts Lookup",
|
||||
icon: faIcon({ icon: "fa-solid fa-rectangle-list" }),
|
||||
},
|
||||
{
|
||||
href: "/interactionsFlow",
|
||||
title: "Interactions Flow",
|
||||
icon: faIcon({ icon: "fa-solid fa-route" }),
|
||||
},
|
||||
{
|
||||
href: "/customerAccount/12345",
|
||||
title: "Customer Account Example",
|
||||
icon: faIcon({ icon: "fa-solid fa-id-card" }),
|
||||
},
|
||||
],
|
||||
collapsed: false,
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.onResize();
|
||||
window.addEventListener("resize", this.onResize);
|
||||
console.log()
|
||||
},
|
||||
methods: {
|
||||
onResize() {
|
||||
if (window.innerWidth <= 767) {
|
||||
this.isOnMobile = true;
|
||||
this.collapsed = true;
|
||||
} else {
|
||||
this.isOnMobile = false;
|
||||
this.collapsed = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="this.$route.query.sidebar" id="sidebar">
|
||||
<sidebar-menu v-model:collapsed="collapsed" :menu="menu" :show-one-child="true" :relative="true"/>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user