diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx
index eecc84cf..cf9c0d04 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx
@@ -24,25 +24,20 @@ export function ListDashboards({ dashboards }: ListDashboardsProps) {
const deletion = api.dashboard.delete.useMutation({
onError: (error, variables) => {
return handleErrorToastOptions({
- action: (
- {
- deletion.mutate({
- forceDelete: true,
- id: variables.id,
- });
- }}
- >
- Force delete
-
- ),
+ action: {
+ label: 'Force delete',
+ onClick: () => {
+ deletion.mutate({
+ forceDelete: true,
+ id: variables.id,
+ });
+ },
+ },
})(error);
},
onSuccess() {
router.refresh();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Dashboard deleted.',
});
},
diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx
index 1e43609a..4b186136 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx
@@ -25,13 +25,12 @@ export default function EditOrganization({
const router = useRouter();
const { register, handleSubmit, formState, reset } = useForm({
- defaultValues: organization,
+ defaultValues: organization ?? undefined,
});
const mutation = api.organization.update.useMutation({
onSuccess(res) {
- toast({
- title: 'Organization updated',
+ toast('Organization updated', {
description: 'Your organization has been updated.',
});
reset(res);
@@ -57,7 +56,7 @@ export default function EditOrganization({
diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/invite-user.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/invite-user.tsx
index 2bae018f..6f8a56d3 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/invite-user.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/organization/invite-user.tsx
@@ -27,8 +27,7 @@ export function InviteUser() {
const mutation = api.organization.inviteUser.useMutation({
onSuccess() {
- toast({
- title: 'User invited!',
+ toast('User invited!', {
description: 'The user has been invited to the organization.',
});
reset();
diff --git a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx
index 292175cc..2d678a66 100644
--- a/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx
+++ b/apps/web/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx
@@ -35,8 +35,7 @@ export default function EditProfile({ profile }: EditProfileProps) {
const mutation = api.user.update.useMutation({
onSuccess(res) {
- toast({
- title: 'Profile updated',
+ toast('Profile updated', {
description: 'Your profile has been updated.',
});
reset(res);
diff --git a/apps/web/src/app/_trpc/client.tsx b/apps/web/src/app/_trpc/client.tsx
index 7e664017..83d94d8c 100644
--- a/apps/web/src/app/_trpc/client.tsx
+++ b/apps/web/src/app/_trpc/client.tsx
@@ -1,8 +1,8 @@
-import type { Toast } from '@/components/ui/use-toast';
import type { AppRouter } from '@/server/api/root';
import type { TRPCClientErrorBase } from '@trpc/react-query';
import { createTRPCReact } from '@trpc/react-query';
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
+import type { ExternalToast } from 'sonner';
import { toast } from 'sonner';
export const api = createTRPCReact({});
@@ -24,16 +24,14 @@ export type IChartData = RouterOutputs['chart']['chart'];
export type IChartSerieDataItem = IChartData['series'][number]['data'][number];
export function handleError(error: TRPCClientErrorBase) {
- toast({
- title: 'Error',
+ toast('Error', {
description: error.message,
});
}
-export function handleErrorToastOptions(options: Toast) {
+export function handleErrorToastOptions(options: ExternalToast) {
return function (error: TRPCClientErrorBase) {
- toast({
- title: 'Error',
+ toast('Error', {
description: error.message,
...options,
});
diff --git a/apps/web/src/app/providers.tsx b/apps/web/src/app/providers.tsx
index 9e401e08..91278087 100644
--- a/apps/web/src/app/providers.tsx
+++ b/apps/web/src/app/providers.tsx
@@ -2,7 +2,6 @@
import React, { useRef, useState } from 'react';
import { api } from '@/app/_trpc/client';
-import { Toaster } from '@/components/ui/toaster';
import { TooltipProvider } from '@/components/ui/tooltip';
import { ModalProvider } from '@/modals';
import type { AppStore } from '@/redux';
@@ -50,7 +49,6 @@ export default function Providers({ children }: { children: React.ReactNode }) {
{children}
-
diff --git a/apps/web/src/components/clients/ClientActions.tsx b/apps/web/src/components/clients/ClientActions.tsx
index ba02ff46..30e831d6 100644
--- a/apps/web/src/components/clients/ClientActions.tsx
+++ b/apps/web/src/components/clients/ClientActions.tsx
@@ -6,6 +6,7 @@ import type { IClientWithProject } from '@/types';
import { clipboard } from '@/utils/clipboard';
import { MoreHorizontal } from 'lucide-react';
import { useRouter } from 'next/navigation';
+import { toast } from 'sonner';
import { Button } from '../ui/button';
import {
@@ -16,15 +17,13 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
-import { toast } from '../ui/use-toast';
export function ClientActions(client: IClientWithProject) {
const { id } = client;
const router = useRouter();
const deletion = api.client.remove.useMutation({
onSuccess() {
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Client revoked, incoming requests will be rejected.',
});
router.refresh();
diff --git a/apps/web/src/components/projects/ProjectActions.tsx b/apps/web/src/components/projects/ProjectActions.tsx
index 1b104d22..0bc3d8ef 100644
--- a/apps/web/src/components/projects/ProjectActions.tsx
+++ b/apps/web/src/components/projects/ProjectActions.tsx
@@ -6,6 +6,7 @@ import type { IProject } from '@/types';
import { clipboard } from '@/utils/clipboard';
import { MoreHorizontal } from 'lucide-react';
import { useRouter } from 'next/navigation';
+import { toast } from 'sonner';
import { Button } from '../ui/button';
import {
@@ -16,15 +17,13 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '../ui/dropdown-menu';
-import { toast } from '../ui/use-toast';
export function ProjectActions(project: IProject) {
const { id } = project;
const router = useRouter();
const deletion = api.project.remove.useMutation({
onSuccess() {
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Project deleted successfully.',
});
router.refresh();
diff --git a/apps/web/src/components/report/ReportSaveButton.tsx b/apps/web/src/components/report/ReportSaveButton.tsx
index fd7ce3bd..9fb874b9 100644
--- a/apps/web/src/components/report/ReportSaveButton.tsx
+++ b/apps/web/src/components/report/ReportSaveButton.tsx
@@ -19,8 +19,7 @@ export function ReportSaveButton({ className }: ReportSaveButtonProps) {
const update = api.report.update.useMutation({
onSuccess() {
dispatch(resetDirty());
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Report updated.',
});
},
diff --git a/apps/web/src/components/ui/sonner.tsx b/apps/web/src/components/ui/sonner.tsx
index 1128edfc..537e52a5 100644
--- a/apps/web/src/components/ui/sonner.tsx
+++ b/apps/web/src/components/ui/sonner.tsx
@@ -1,29 +1,29 @@
-import { useTheme } from "next-themes"
-import { Toaster as Sonner } from "sonner"
+import { useTheme } from 'next-themes';
+import { Toaster as Sonner } from 'sonner';
-type ToasterProps = React.ComponentProps
+type ToasterProps = React.ComponentProps;
const Toaster = ({ ...props }: ToasterProps) => {
- const { theme = "system" } = useTheme()
+ const { theme = 'system' } = useTheme();
return (
- )
-}
+ );
+};
-export { Toaster }
+export { Toaster };
diff --git a/apps/web/src/components/ui/use-toast.ts b/apps/web/src/components/ui/use-toast.ts
deleted file mode 100644
index e6807d07..00000000
--- a/apps/web/src/components/ui/use-toast.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-'use client';
-
-// Inspired by react-hot-toast library
-import * as React from 'react';
-import type { ToastActionElement, ToastProps } from '@/components/ui/toast';
-
-const TOAST_LIMIT = 1;
-const TOAST_REMOVE_DELAY = 1000000;
-
-type ToasterToast = ToastProps & {
- id: string;
- title?: React.ReactNode;
- description?: React.ReactNode;
- action?: ToastActionElement;
-};
-
-const actionTypes = {
- ADD_TOAST: 'ADD_TOAST',
- UPDATE_TOAST: 'UPDATE_TOAST',
- DISMISS_TOAST: 'DISMISS_TOAST',
- REMOVE_TOAST: 'REMOVE_TOAST',
-} as const;
-
-let count = 0;
-
-function genId() {
- count = (count + 1) % Number.MAX_VALUE;
- return count.toString();
-}
-
-type ActionType = typeof actionTypes;
-
-type Action =
- | {
- type: ActionType['ADD_TOAST'];
- toast: ToasterToast;
- }
- | {
- type: ActionType['UPDATE_TOAST'];
- toast: Partial;
- }
- | {
- type: ActionType['DISMISS_TOAST'];
- toastId?: ToasterToast['id'];
- }
- | {
- type: ActionType['REMOVE_TOAST'];
- toastId?: ToasterToast['id'];
- };
-
-interface State {
- toasts: ToasterToast[];
-}
-
-const toastTimeouts = new Map>();
-
-const addToRemoveQueue = (toastId: string) => {
- if (toastTimeouts.has(toastId)) {
- return;
- }
-
- const timeout = setTimeout(() => {
- toastTimeouts.delete(toastId);
- dispatch({
- type: 'REMOVE_TOAST',
- toastId: toastId,
- });
- }, TOAST_REMOVE_DELAY);
-
- toastTimeouts.set(toastId, timeout);
-};
-
-export const reducer = (state: State, action: Action): State => {
- switch (action.type) {
- case 'ADD_TOAST':
- return {
- ...state,
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
- };
-
- case 'UPDATE_TOAST':
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === action.toast.id ? { ...t, ...action.toast } : t
- ),
- };
-
- case 'DISMISS_TOAST': {
- const { toastId } = action;
-
- // ! Side effects ! - This could be extracted into a dismissToast() action,
- // but I'll keep it here for simplicity
- if (toastId) {
- addToRemoveQueue(toastId);
- } else {
- state.toasts.forEach((toast) => {
- addToRemoveQueue(toast.id);
- });
- }
-
- return {
- ...state,
- toasts: state.toasts.map((t) =>
- t.id === toastId || toastId === undefined
- ? {
- ...t,
- open: false,
- }
- : t
- ),
- };
- }
- case 'REMOVE_TOAST':
- if (action.toastId === undefined) {
- return {
- ...state,
- toasts: [],
- };
- }
- return {
- ...state,
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
- };
- }
-};
-
-const listeners: ((state: State) => void)[] = [];
-
-let memoryState: State = { toasts: [] };
-
-function dispatch(action: Action) {
- memoryState = reducer(memoryState, action);
- listeners.forEach((listener) => {
- listener(memoryState);
- });
-}
-
-export type Toast = Omit;
-
-function toast({ ...props }: Toast) {
- const id = genId();
-
- const update = (props: ToasterToast) =>
- dispatch({
- type: 'UPDATE_TOAST',
- toast: { ...props, id },
- });
- const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id });
-
- dispatch({
- type: 'ADD_TOAST',
- toast: {
- ...props,
- id,
- open: true,
- onOpenChange: (open) => {
- if (!open) dismiss();
- },
- },
- });
-
- return {
- id: id,
- dismiss,
- update,
- };
-}
-
-function useToast() {
- const [state, setState] = React.useState(memoryState);
-
- React.useEffect(() => {
- listeners.push(setState);
- return () => {
- const index = listeners.indexOf(setState);
- if (index > -1) {
- listeners.splice(index, 1);
- }
- };
- }, [state]);
-
- return {
- ...state,
- toast,
- dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
- };
-}
-
-export { useToast, toast };
diff --git a/apps/web/src/modals/AddClient.tsx b/apps/web/src/modals/AddClient.tsx
index fa56d455..15d03ba9 100644
--- a/apps/web/src/modals/AddClient.tsx
+++ b/apps/web/src/modals/AddClient.tsx
@@ -40,8 +40,7 @@ export default function AddClient({ organizationId }: AddClientProps) {
const mutation = api.client.create.useMutation({
onError: handleError,
onSuccess() {
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Client created!',
});
router.refresh();
diff --git a/apps/web/src/modals/AddDashboard.tsx b/apps/web/src/modals/AddDashboard.tsx
index af5412e6..fde7d54a 100644
--- a/apps/web/src/modals/AddDashboard.tsx
+++ b/apps/web/src/modals/AddDashboard.tsx
@@ -35,8 +35,7 @@ export default function AddDashboard() {
onError: handleError,
onSuccess() {
router.refresh();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Dashboard created.',
});
popModal();
diff --git a/apps/web/src/modals/AddProject.tsx b/apps/web/src/modals/AddProject.tsx
index d6f3c658..6ee2c799 100644
--- a/apps/web/src/modals/AddProject.tsx
+++ b/apps/web/src/modals/AddProject.tsx
@@ -27,8 +27,7 @@ export default function AddProject({ organizationId }: AddProjectProps) {
onError: handleError,
onSuccess() {
router.refresh();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Project created! Lets create a client for it 🤘',
});
popModal();
diff --git a/apps/web/src/modals/EditClient.tsx b/apps/web/src/modals/EditClient.tsx
index 37435372..dcebc6b1 100644
--- a/apps/web/src/modals/EditClient.tsx
+++ b/apps/web/src/modals/EditClient.tsx
@@ -39,8 +39,7 @@ export default function EditClient({ id, name, cors }: EditClientProps) {
onError: handleError,
onSuccess() {
reset();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Client updated.',
});
popModal();
diff --git a/apps/web/src/modals/EditDashboard.tsx b/apps/web/src/modals/EditDashboard.tsx
index b90634f5..b0f3b3a0 100644
--- a/apps/web/src/modals/EditDashboard.tsx
+++ b/apps/web/src/modals/EditDashboard.tsx
@@ -37,8 +37,7 @@ export default function EditDashboard({ id, name }: EditDashboardProps) {
onError: handleError,
onSuccess() {
reset();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Dashboard updated.',
});
popModal();
diff --git a/apps/web/src/modals/EditProject.tsx b/apps/web/src/modals/EditProject.tsx
index d25abfd7..37767c11 100644
--- a/apps/web/src/modals/EditProject.tsx
+++ b/apps/web/src/modals/EditProject.tsx
@@ -38,8 +38,7 @@ export default function EditProject({ id, name }: EditProjectProps) {
onSuccess() {
reset();
router.refresh();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Project updated.',
});
popModal();
diff --git a/apps/web/src/modals/SaveReport.tsx b/apps/web/src/modals/SaveReport.tsx
index 0941a37e..ab1ab34d 100644
--- a/apps/web/src/modals/SaveReport.tsx
+++ b/apps/web/src/modals/SaveReport.tsx
@@ -38,8 +38,7 @@ export default function SaveReport({ report }: SaveReportProps) {
const save = api.report.save.useMutation({
onError: handleError,
onSuccess(res) {
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Report saved.',
});
popModal();
@@ -65,8 +64,7 @@ export default function SaveReport({ report }: SaveReportProps) {
onSuccess(res) {
setValue('dashboardId', res.id);
dashboardQuery.refetch();
- toast({
- title: 'Success',
+ toast('Success', {
description: 'Dashboard created.',
});
},
diff --git a/apps/web/src/utils/clipboard.ts b/apps/web/src/utils/clipboard.ts
index 171fc623..02bf3ba8 100644
--- a/apps/web/src/utils/clipboard.ts
+++ b/apps/web/src/utils/clipboard.ts
@@ -2,8 +2,7 @@ import { toast } from 'sonner';
export function clipboard(value: string | number) {
navigator.clipboard.writeText(value.toString());
- toast({
- title: 'Copied to clipboard',
+ toast('Copied to clipboard', {
description: value.toString(),
});
}
diff --git a/packages/queue/src/connection.ts b/packages/queue/src/connection.ts
index d3d65c0d..60a50b38 100644
--- a/packages/queue/src/connection.ts
+++ b/packages/queue/src/connection.ts
@@ -1,12 +1,9 @@
const parse = (connectionString: string) => {
- const match = connectionString.match(/redis:\/\/(.+?):(.+?)@(.+?):(.+)/);
- if (!match) {
- throw new Error('Invalid connection string');
- }
+ const url = new URL(connectionString);
return {
- host: match[3]!,
- port: Number(match[4]),
- password: match[2]!,
+ host: url.hostname,
+ port: Number(url.port),
+ password: url.password,
} as const;
};