Skip to content

Commit

Permalink
Sets up toast ui to display GraphQL Errors (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
amorey committed May 2, 2024
1 parent 1bcf363 commit 7098155
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 30 deletions.
6 changes: 3 additions & 3 deletions frontend/src/apollo-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { RetryLink } from '@apollo/client/link/retry';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { ClientOptions, createClient } from 'graphql-ws';
import toast from 'react-hot-toast';

import generatedIntrospection from '@/lib/graphql/__generated__/introspection-result.json';
import { getBasename, getCSRFToken, joinPaths } from './lib/helpers';
Expand Down Expand Up @@ -66,9 +67,9 @@ const errorLink = onError(({ graphQLErrors, networkError }) => {
}

if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) => {
graphQLErrors.forEach(({ message, path }) => {
const msg = `[GraphQL Error] ${message}`;
console.error(`${msg} (Loc: ${locations}, Path: ${path})`);
toast(msg, { id: `${path}` });
});
}
});
Expand All @@ -82,7 +83,6 @@ const retryLink = new RetryLink({
attempts: {
max: Infinity,
retryIf: (error) => {
console.log('retryIf');
console.log(error);
return true;
},
Expand Down
93 changes: 72 additions & 21 deletions frontend/src/pages/_root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,84 @@
// limitations under the License.

import { XCircleIcon } from '@heroicons/react/24/outline';
import { useEffect } from 'react';
import { Toaster, resolveValue } from 'react-hot-toast';
import { useEffect, useState } from 'react';
import toastlib, { useToaster, resolveValue } from 'react-hot-toast';
import type { Toast } from 'react-hot-toast';
import { Outlet } from 'react-router-dom';

import Button from '@kubetail/ui/elements/Button';

import Modal from '@/components/elements/Modal';
import { joinPaths, getBasename } from '@/lib/helpers';

const CustomToaster = () => (
<Toaster position="bottom-center">
{(t) => (
<div className="rounded-md bg-red-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
Error
</h3>
<div className="mt-2 text-sm text-red-700">
{resolveValue(t.message, t)}
</div>
const QueryError = ({ toast }: { toast: Toast }) => (
<div className="relative bg-red-100 border-2 border-red-200 p-1">
<button
type="button"
className="absolute top-[-10px] left-[-10px] bg-red-100 rounded-full cursor-pointer"
onClick={() => toastlib.remove(toast.id)}
aria-label="remove"
>
<XCircleIcon
className="h-5 w-5 text-red-300 hover:text-red-400"
aria-hidden="true"
/>
</button>
{resolveValue(toast.message, toast)}
</div>
);

const CustomToaster = () => {
const { toasts } = useToaster();
const [modalIsOpen, setModalIsOpen] = useState(false);

if (!toasts.length) return null;

const handleRemoveAllClick = () => {
toastlib.remove();
};

return (
<div className="fixed bottom-[28px] right-[5px] p-2 bg-red-100 border-2 border-red-200">
<button
type="button"
className="absolute top-[-10px] left-[-10px] bg-red-100 rounded-full cursor-pointer"
onClick={handleRemoveAllClick}
aria-label="remove all"
>
<XCircleIcon
className="h-5 w-5 text-red-300 hover:text-red-400"
aria-hidden="true"
/>
</button>
<button
type="button"
onClick={() => setModalIsOpen(true)}
>
Query Errors
{`(${toasts.length})`}
</button>
<Modal
className="max-w-[500px]"
open={modalIsOpen}
onClose={() => setModalIsOpen(false)}
>
<Modal.Title>
<div className="flex justify-between">
<span>
Query Errors
{`(${toasts.length})`}
</span>
<Button intent="danger" size="xs" onClick={() => toastlib.remove()}>Dismiss All</Button>
</div>
</Modal.Title>
<div className="text-sm space-y-4">
{toasts.map((toast) => <QueryError toast={toast} />)}
</div>
</div>
)}
</Toaster>
);
</Modal>
</div>
);
};

export default function Root() {
// update favicon location
Expand Down
6 changes: 0 additions & 6 deletions backend/hack/minikube.yaml → hack/minikube.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
---
# Source: kubetail/templates/service-account.yaml
kind: ServiceAccount
apiVersion: v1
automountServiceAccountToken: true
Expand All @@ -8,7 +7,6 @@ metadata:
namespace: kubetail
annotations:
---
# Source: kubetail/templates/config-map.yaml
kind: ConfigMap
apiVersion: v1
metadata:
Expand All @@ -24,7 +22,6 @@ data:
csrf:
secret: REPLACEME
---
# Source: kubetail/templates/cluster-role.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
Expand All @@ -35,7 +32,6 @@ rules:
resources: [cronjobs, daemonsets, deployments, jobs, namespaces, nodes, pods, pods/log, replicasets, statefulsets]
verbs: [get, list, watch]
---
# Source: kubetail/templates/cluster-role-binding.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
Expand All @@ -50,7 +46,6 @@ subjects:
name: kubetail
namespace: kubetail
---
# Source: kubetail/templates/service.yaml
kind: Service
apiVersion: v1
metadata:
Expand All @@ -69,7 +64,6 @@ spec:
port: 80
targetPort: kubetail
---
# Source: kubetail/templates/deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
Expand Down

0 comments on commit 7098155

Please sign in to comment.