diff --git a/apps/web/app/dashboard4/(sidebar)/blog/[id]/page.tsx b/apps/web/app/dashboard4/(sidebar)/blog/[id]/page.tsx index 4ffcfc3bd..db44f9c21 100644 --- a/apps/web/app/dashboard4/(sidebar)/blog/[id]/page.tsx +++ b/apps/web/app/dashboard4/(sidebar)/blog/[id]/page.tsx @@ -12,6 +12,7 @@ import { MenuItem, Skeleton, Tabbs, + useToast, } from "@courselit/components-library"; import { MoreVert } from "@courselit/icons"; import { @@ -41,6 +42,7 @@ export default function Page({ params }: { params: { id: string } }) { const profile = useContext(ProfileContext); const course = useCourse(id, address); const router = useRouter(); + const { toast } = useToast(); return ( @@ -83,6 +85,7 @@ export default function Page({ params }: { params: { id: string } }) { `/dashboard4/blogs`, ); }, + toast, }) } > diff --git a/apps/web/app/dashboard4/(sidebar)/my-content/page.tsx b/apps/web/app/dashboard4/(sidebar)/my-content/page.tsx index 90e49f1c8..ade51be51 100644 --- a/apps/web/app/dashboard4/(sidebar)/my-content/page.tsx +++ b/apps/web/app/dashboard4/(sidebar)/my-content/page.tsx @@ -7,11 +7,13 @@ import { Link, Section, Skeleton, + useToast, } from "@courselit/components-library"; import { FetchBuilder } from "@courselit/utils"; import { ACCOUNT_NO_PURCHASE_PLACEHOLDER, ACCOUNT_PROGRESS_SUFFIX, + ERROR_SNACKBAR_PREFIX, MY_CONTENT_HEADER, VISIT_COURSE_BUTTON, } from "@ui-config/strings"; @@ -22,6 +24,7 @@ const breadcrumbs = [{ label: MY_CONTENT_HEADER, href: "#" }]; export default function Page() { const [courses, setCourses] = useState([]); const [loaded, setLoaded] = useState(false); + const { toast } = useToast(); const profile = useContext(ProfileContext); const address = useContext(AddressContext); @@ -50,7 +53,12 @@ export default function Page() { setCourses(response.courses); } setLoaded(true); - } catch (e: any) {} + } catch (e: any) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); + } }; loadEnrolledCourses(); diff --git a/apps/web/app/dashboard4/(sidebar)/profile/page.tsx b/apps/web/app/dashboard4/(sidebar)/profile/page.tsx index 293c6c156..0819818fc 100644 --- a/apps/web/app/dashboard4/(sidebar)/profile/page.tsx +++ b/apps/web/app/dashboard4/(sidebar)/profile/page.tsx @@ -15,10 +15,13 @@ import { MediaSelector, PageBuilderPropertyHeader, Section, + useToast, } from "@courselit/components-library"; import { FetchBuilder } from "@courselit/utils"; import { + APP_MESSAGE_CHANGES_SAVED, BUTTON_SAVE, + ERROR_SNACKBAR_PREFIX, MEDIA_SELECTOR_REMOVE_BTN_CAPTION, MEDIA_SELECTOR_UPLOAD_BTN_CAPTION, PROFILE_EMAIL_PREFERENCES, @@ -41,6 +44,7 @@ export default function Page() { useState>(); const [avatar, setAvatar] = useState>({}); const [subscribedToUpdates, setSubscribedToUpdates] = useState(false); + const { toast } = useToast(); const profile = useContext(ProfileContext); const address = useContext(AddressContext); @@ -83,7 +87,10 @@ export default function Page() { setSubscribedToUpdates(response.user.subscribedToUpdates); } } catch (err: any) { - console.error(`Profile page: ${err.message}`); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }; if (profile.userId && address.backend) { @@ -128,9 +135,15 @@ export default function Page() { try { await fetch.exec(); + toast({ + title: "", + description: APP_MESSAGE_CHANGES_SAVED, + }); } catch (err: any) { - console.error(err); - } finally { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }; @@ -175,9 +188,15 @@ export default function Page() { try { await fetch.exec(); + toast({ + title: "", + description: APP_MESSAGE_CHANGES_SAVED, + }); } catch (err: any) { - console.error(err); - } finally { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }; @@ -209,8 +228,10 @@ export default function Page() { await fetch.exec(); } catch (err: any) { setSubscribedToUpdates(!state); - console.error(err); - } finally { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }; diff --git a/apps/web/app/dashboard4/(sidebar)/users/[id]/page.tsx b/apps/web/app/dashboard4/(sidebar)/users/[id]/page.tsx index f6f6b7e3f..a093034f9 100644 --- a/apps/web/app/dashboard4/(sidebar)/users/[id]/page.tsx +++ b/apps/web/app/dashboard4/(sidebar)/users/[id]/page.tsx @@ -4,9 +4,16 @@ import DashboardContent from "@components/admin/dashboard-content"; import { PermissionsEditor } from "@components/admin/users/permissions-editor"; import { AddressContext } from "@components/contexts"; import { UserWithAdminFields } from "@courselit/common-models"; -import { ComboBox, Link, Section, Switch } from "@courselit/components-library"; +import { + ComboBox, + Link, + Section, + Switch, + useToast, +} from "@courselit/components-library"; import { FetchBuilder } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, PAGE_HEADER_ALL_USER, PAGE_HEADER_EDIT_USER, SWITCH_ACCOUNT_ACTIVE, @@ -28,6 +35,7 @@ export default function Page({ params }: { params: { id: string } }) { const [tags, setTags] = useState([]); const address = useContext(AddressContext); const { id } = params; + const { toast } = useToast(); useEffect(() => { getUserDetails(); @@ -49,7 +57,12 @@ export default function Page({ params }: { params: { id: string } }) { if (response.tags) { setTags(response.tags); } - } catch (err) {} + } catch (err) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }, [address.backend]); useEffect(() => { @@ -86,7 +99,12 @@ export default function Page({ params }: { params: { id: string } }) { if (response.user) { setUserData(response.user); } - } catch (err: any) {} + } catch (err: any) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }; // TODO: test this method. A hard-coded userId was there in the query. @@ -107,7 +125,12 @@ export default function Page({ params }: { params: { id: string } }) { try { const response = await fetch.exec(); setEnrolledCourses(response.enrolledCourses); - } catch (err) {} + } catch (err) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }; const toggleActiveState = async (value: boolean) => { @@ -137,7 +160,12 @@ export default function Page({ params }: { params: { id: string } }) { if (response.user) { setUserData(response.user); } - } catch (err: any) {} + } catch (err: any) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }; const updateTags = async (tags: string[]) => { @@ -173,7 +201,12 @@ export default function Page({ params }: { params: { id: string } }) { if (response.user) { setUserData(response.user); } - } catch (err: any) {} + } catch (err: any) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }; if (!userData) { diff --git a/apps/web/app/dashboard4/(sidebar)/users/tags/new/page.tsx b/apps/web/app/dashboard4/(sidebar)/users/tags/new/page.tsx index 002dccda0..16fcad009 100644 --- a/apps/web/app/dashboard4/(sidebar)/users/tags/new/page.tsx +++ b/apps/web/app/dashboard4/(sidebar)/users/tags/new/page.tsx @@ -1,11 +1,17 @@ "use client"; import React, { useState, ChangeEvent, useContext } from "react"; -import { Button, Form, FormField } from "@courselit/components-library"; +import { + Button, + Form, + FormField, + useToast, +} from "@courselit/components-library"; import { BTN_CONTINUE, BTN_NEW_TAG, BUTTON_CANCEL_TEXT, + ERROR_SNACKBAR_PREFIX, USERS_MANAGER_PAGE_HEADING, USERS_TAG_HEADER, USERS_TAG_NEW_HEADER, @@ -41,6 +47,7 @@ export default function Page() { const [name, setName] = useState(""); const [loading, setLoading] = useState(false); const router = useRouter(); + const { toast } = useToast(); const profile = useContext(ProfileContext); const createTag = async (e: FormEvent) => { @@ -68,6 +75,10 @@ export default function Page() { router.replace("/dashboard4/users/tags"); } } catch (err: any) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { setLoading(false); } diff --git a/apps/web/app/dashboard4/(sidebar)/users/users-hub.tsx b/apps/web/app/dashboard4/(sidebar)/users/users-hub.tsx index 7bd6cdca8..4ed7e76f5 100644 --- a/apps/web/app/dashboard4/(sidebar)/users/users-hub.tsx +++ b/apps/web/app/dashboard4/(sidebar)/users/users-hub.tsx @@ -19,9 +19,11 @@ import { TableBody, TableHead, TableRow, + useToast, } from "@courselit/components-library"; import { checkPermission, FetchBuilder } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, USER_TABLE_HEADER_JOINED, USER_TABLE_HEADER_LAST_ACTIVE, USER_TABLE_HEADER_NAME, @@ -44,6 +46,7 @@ export default function UsersHub() { const [filtersAggregator, setFiltersAggregator] = useState("or"); const [count, setCount] = useState(0); + const { toast } = useToast(); const profile = useContext(ProfileContext); @@ -130,10 +133,11 @@ export default function UsersHub() { setCount(response.count); } } catch (err) { - console.error(err); - } finally { - } - }, [address.backend, page, rowsPerPage, filters, filtersAggregator]); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + }}, [address.backend, page, rowsPerPage, filters, filtersAggregator]); useEffect(() => { loadUsers(); diff --git a/apps/web/app/dashboard4/layout-with-context.tsx b/apps/web/app/dashboard4/layout-with-context.tsx index 7ab1a05b6..e211bb968 100644 --- a/apps/web/app/dashboard4/layout-with-context.tsx +++ b/apps/web/app/dashboard4/layout-with-context.tsx @@ -11,7 +11,8 @@ import { TypefacesContext, ServerConfigContext, } from "@components/contexts"; -import { Toaster } from "@courselit/components-library"; +import { Toaster, useToast } from "@courselit/components-library"; +import { ERROR_SNACKBAR_PREFIX } from "@ui-config/strings"; export default function Layout({ address, @@ -27,6 +28,7 @@ export default function Layout({ config: ServerConfig; }) { const [profile, setProfile] = useState(defaultState.profile); + const { toast } = useToast(); useEffect(() => { const getUserProfile = async () => { @@ -66,7 +68,12 @@ export default function Layout({ if (response.profile) { setProfile(response.profile); } - } catch (e) {} + } catch (err) { + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); + } }; if (address) { diff --git a/apps/web/components/admin/blogs/blog-item.tsx b/apps/web/components/admin/blogs/blog-item.tsx index 5f01a98e5..6ea49c2eb 100644 --- a/apps/web/components/admin/blogs/blog-item.tsx +++ b/apps/web/components/admin/blogs/blog-item.tsx @@ -11,7 +11,13 @@ import { MoreVert } from "@courselit/icons"; import type { AppDispatch } from "@courselit/state-management"; import type { SiteInfo, Address } from "@courselit/common-models"; // import { connect } from "react-redux"; -import { Chip, Menu2, MenuItem, Link } from "@courselit/components-library"; +import { + Chip, + Menu2, + MenuItem, + Link, + useToast, +} from "@courselit/components-library"; import { deleteProduct } from "./helpers"; import { TableRow } from "@courselit/components-library"; @@ -34,6 +40,7 @@ export default function BlogItem({ prefix: string; }) { const product = details; + const { toast } = useToast(); return ( @@ -76,6 +83,7 @@ export default function BlogItem({ onDeleteComplete: () => { onDelete(position); }, + toast, }) } > diff --git a/apps/web/components/admin/blogs/editor/course-hook.ts b/apps/web/components/admin/blogs/editor/course-hook.ts index 3f8c75fc8..e08ede8aa 100644 --- a/apps/web/components/admin/blogs/editor/course-hook.ts +++ b/apps/web/components/admin/blogs/editor/course-hook.ts @@ -1,10 +1,9 @@ -import { Address, AppMessage, Course } from "@courselit/common-models"; +import { Address, Course } from "@courselit/common-models"; +import { useToast } from "@courselit/components-library"; import { AppDispatch /*AppState*/ } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; +import { ERROR_SNACKBAR_PREFIX } from "@ui-config/strings"; import { useCallback, useEffect, useState } from "react"; type CourseWithAdminProps = Partial< @@ -23,6 +22,7 @@ export default function useCourse( const [course, setCourse] = useState< CourseWithAdminProps | undefined | null >(); + const { toast } = useToast(); const loadCourse = useCallback( async (courseId: string) => { @@ -64,8 +64,10 @@ export default function useCourse( } } catch (err: any) { setCourse(null); - dispatch && - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/blogs/editor/details.tsx b/apps/web/components/admin/blogs/editor/details.tsx index 37afe754d..8c2c47337 100644 --- a/apps/web/components/admin/blogs/editor/details.tsx +++ b/apps/web/components/admin/blogs/editor/details.tsx @@ -7,18 +7,17 @@ import { FormField, Button, PageBuilderPropertyHeader, + useToast, } from "@courselit/components-library"; import useCourse from "./course-hook"; import { FetchBuilder } from "@courselit/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; -import { Address, AppMessage, Profile } from "@courselit/common-models"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; +import { Address, Profile } from "@courselit/common-models"; import { APP_MESSAGE_COURSE_SAVED, BUTTON_SAVE, COURSE_CONTENT_HEADER, + ERROR_SNACKBAR_PREFIX, FORM_FIELD_FEATURED_IMAGE, } from "../../../../ui-config/strings"; import { connect } from "react-redux"; @@ -39,6 +38,7 @@ export function Details({ id, address, dispatch, profile }: DetailsProps) { const [featuredImage, setFeaturedImage] = useState>({}); const [refreshDetails, setRefreshDetails] = useState(0); const course = useCourse(id, address, dispatch); + const { toast } = useToast(); useEffect(() => { if (course) { @@ -76,13 +76,16 @@ export function Details({ id, address, dispatch, profile }: DetailsProps) { dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateCourse) { - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_SAVED, + }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -114,13 +117,16 @@ export function Details({ id, address, dispatch, profile }: DetailsProps) { dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateCourse) { - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_SAVED, + }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/blogs/editor/layout/header.tsx b/apps/web/components/admin/blogs/editor/layout/header.tsx index 73f4acf78..d5b5c4418 100644 --- a/apps/web/components/admin/blogs/editor/layout/header.tsx +++ b/apps/web/components/admin/blogs/editor/layout/header.tsx @@ -6,6 +6,7 @@ import { Menu2, Link, Breadcrumbs, + useToast, } from "@courselit/components-library"; import { DELETE_PRODUCT_POPUP_HEADER, @@ -42,6 +43,7 @@ export default function BlogHeader({ }: BlogHeaderProps) { const course = useCourse(id, address); const router = useRouter(); + const { toast } = useToast(); if (!course) { return <>; @@ -93,6 +95,7 @@ export default function BlogHeader({ onDeleteComplete: () => { router.replace(`/dashboard/blogs`); }, + toast, }) } > diff --git a/apps/web/components/admin/blogs/editor/publish.tsx b/apps/web/components/admin/blogs/editor/publish.tsx index b525e61e7..9b9f88905 100644 --- a/apps/web/components/admin/blogs/editor/publish.tsx +++ b/apps/web/components/admin/blogs/editor/publish.tsx @@ -1,16 +1,14 @@ import React, { FormEvent, useEffect, useState } from "react"; -import { Button, Form } from "@courselit/components-library"; +import { Button, Form, useToast } from "@courselit/components-library"; import useCourse from "./course-hook"; import { connect } from "react-redux"; import { capitalize, FetchBuilder } from "@courselit/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; -import { Address, AppMessage } from "@courselit/common-models"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; +import { Address } from "@courselit/common-models"; import { BTN_PUBLISH, BTN_UNPUBLISH, + ERROR_SNACKBAR_PREFIX, PUBLISH_TAB_STATUS_SUBTITLE, PUBLISH_TAB_STATUS_TITLE, PUBLISH_TAB_VISIBILITY_SUBTITLE, @@ -29,6 +27,7 @@ export function Publish({ id, address, dispatch, loading }: PublishProps) { let course = useCourse(id, address, dispatch); const [published, setPublished] = useState(course?.published); const [privacy, setPrivacy] = useState(course?.privacy); + const { toast } = useToast(); useEffect(() => { if (course) { @@ -86,7 +85,10 @@ export function Publish({ id, address, dispatch, loading }: PublishProps) { return response.course; } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/blogs/helpers.ts b/apps/web/components/admin/blogs/helpers.ts index aa0b10195..68abdc823 100644 --- a/apps/web/components/admin/blogs/helpers.ts +++ b/apps/web/components/admin/blogs/helpers.ts @@ -1,23 +1,25 @@ -import { AppMessage } from "@courselit/common-models"; import { AppDispatch } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; -import { APP_MESSAGE_COURSE_DELETED } from "../../../ui-config/strings"; +import { + APP_MESSAGE_COURSE_DELETED, + ERROR_SNACKBAR_PREFIX, +} from "../../../ui-config/strings"; interface DeleteProductProps { id: string; backend: string; dispatch?: AppDispatch; onDeleteComplete?: (...args: any[]) => void; + toast: (options: { title: string; description: string }) => void; } + export const deleteProduct = async ({ id, backend, dispatch, onDeleteComplete, + toast, }: DeleteProductProps) => { const query = ` mutation { @@ -40,11 +42,17 @@ export const deleteProduct = async ({ // onDelete(position); } } catch (err: any) { - console.error(err); - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast && + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); - dispatch && - dispatch(setAppMessage(new AppMessage(APP_MESSAGE_COURSE_DELETED))); + toast && + toast({ + title: "", + description: APP_MESSAGE_COURSE_DELETED, + }); } }; diff --git a/apps/web/components/admin/blogs/index.tsx b/apps/web/components/admin/blogs/index.tsx index 2aeaa1cd7..ce112bf96 100644 --- a/apps/web/components/admin/blogs/index.tsx +++ b/apps/web/components/admin/blogs/index.tsx @@ -1,20 +1,13 @@ import React, { useCallback, useEffect, useState } from "react"; -import { - Address, - AppMessage, - Course, - SiteInfo, -} from "@courselit/common-models"; +import { Address, Course, SiteInfo } from "@courselit/common-models"; import { AppDispatch, AppState } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { connect } from "react-redux"; import { BLOG_TABLE_HEADER_NAME, BTN_NEW_BLOG, + ERROR_SNACKBAR_PREFIX, MANAGE_BLOG_PAGE_HEADING, PRODUCTS_TABLE_HEADER_ACTIONS, PRODUCTS_TABLE_HEADER_STATUS, @@ -22,10 +15,16 @@ import { } from "../../../ui-config/strings"; import dynamic from "next/dynamic"; import { MoreVert } from "@courselit/icons"; -import { MenuItem, Menu2, Button, Link } from "@courselit/components-library"; -import { Table } from "@courselit/components-library"; -import { TableHead } from "@courselit/components-library"; -import { TableBody } from "@courselit/components-library"; +import { + MenuItem, + Menu2, + Button, + Link, + Table, + TableHead, + TableBody, + useToast, +} from "@courselit/components-library"; import { usePathname } from "next/navigation"; const BlogItem = dynamic(() => import("./blog-item")); @@ -51,6 +50,7 @@ export const Index = ({ >([]); const [endReached, setEndReached] = useState(false); const path = usePathname(); + const { toast } = useToast(); const loadBlogs = useCallback(async () => { const query = ` @@ -85,7 +85,10 @@ export const Index = ({ } } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/blogs/new-blog.tsx b/apps/web/components/admin/blogs/new-blog.tsx index 00fd3fd13..0bdec0b79 100644 --- a/apps/web/components/admin/blogs/new-blog.tsx +++ b/apps/web/components/admin/blogs/new-blog.tsx @@ -1,10 +1,11 @@ import React, { FormEvent, useState } from "react"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Form, FormField, Button, Breadcrumbs, + useToast, } from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; @@ -15,13 +16,11 @@ import { BTN_NEW_BLOG, BUTTON_CANCEL_TEXT, COURSE_TYPE_BLOG, + ERROR_SNACKBAR_PREFIX, FORM_NEW_PRODUCT_TITLE_PLC, MANAGE_BLOG_PAGE_HEADING, } from "@/ui-config/strings"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import Link from "next/link"; interface NewBlogProps { @@ -39,6 +38,7 @@ export function NewBlog({ }: NewBlogProps) { const [title, setTitle] = useState(""); const router = useRouter(); + const { toast } = useToast(); const createCourse = async (e: FormEvent) => { e.preventDefault(); @@ -70,7 +70,10 @@ export function NewBlog({ ); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/mails/broadcast-editor.tsx b/apps/web/components/admin/mails/broadcast-editor.tsx index 7f0a58233..d4192a1a5 100644 --- a/apps/web/components/admin/mails/broadcast-editor.tsx +++ b/apps/web/components/admin/mails/broadcast-editor.tsx @@ -4,14 +4,14 @@ import { FormField, Breadcrumbs, Link, + Dialog2, + useToast, } from "@courselit/components-library"; -import { AppDispatch } from "@courselit/state-management"; +import { AppDispatch, actionCreators } from "@courselit/state-management"; import { ChangeEvent, FormEvent, useEffect, useState } from "react"; -import { actionCreators } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; import { Address, - AppMessage, Constants, SequenceReport, UserFilter, @@ -24,6 +24,7 @@ import { BUTTON_CANCEL_TEXT, DIALOG_SEND_HEADER, ERROR_DELAY_EMPTY, + ERROR_SNACKBAR_PREFIX, ERROR_SUBJECT_EMPTY, FORM_MAIL_SCHEDULE_TIME_LABEL, MAIL_SUBJECT_PLACEHOLDER, @@ -31,12 +32,10 @@ import { PAGE_HEADER_EDIT_MAIL, TOAST_MAIL_SENT, } from "@ui-config/strings"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import FilterContainer from "@components/admin/users/filter-container"; import { useCallback } from "react"; import { useMemo } from "react"; import { PaperPlane, Clock } from "@courselit/icons"; -import { Dialog2 } from "@courselit/components-library"; import { isDateInFuture } from "../../../lib/utils"; import { MailEditorAndPreview } from "./mail-editor-and-preview"; const { networkAction } = actionCreators; @@ -63,6 +62,7 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); const [report, setReport] = useState(); const [status, setStatus] = useState(null); + const { toast } = useToast(); const fetch = useMemo( () => @@ -127,7 +127,10 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); setLoaded(true); @@ -211,7 +214,10 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { dispatch && dispatch(networkAction(true)); await fetcher.exec(); } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -235,15 +241,19 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { e.preventDefault(); if (!subject.trim()) { - dispatch && - dispatch(setAppMessage(new AppMessage(ERROR_SUBJECT_EMPTY))); + toast({ + title: "", + description: ERROR_SUBJECT_EMPTY, + }); setConfirmationDialogOpen(false); return; } if (sendLater && delay === 0) { - dispatch && - dispatch(setAppMessage(new AppMessage(ERROR_DELAY_EMPTY))); + toast({ + title: "", + description: ERROR_DELAY_EMPTY, + }); setConfirmationDialogOpen(false); return; } @@ -320,11 +330,16 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { setReport(sequence.report); setStatus(sequence.status); setShowScheduleInput(false); - dispatch && - dispatch(setAppMessage(new AppMessage(TOAST_MAIL_SENT))); + toast({ + title: "", + description: TOAST_MAIL_SENT, + }); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); setConfirmationDialogOpen(false); @@ -395,7 +410,10 @@ function MailEditor({ id, address, dispatch, prefix }: MailEditorProps) { setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/mails/index.tsx b/apps/web/components/admin/mails/index.tsx index 0464efa40..cf03e18a2 100644 --- a/apps/web/components/admin/mails/index.tsx +++ b/apps/web/components/admin/mails/index.tsx @@ -1,23 +1,25 @@ import React, { useEffect, useState } from "react"; import { Address, - AppMessage, Constants, Domain, Sequence, SequenceType, } from "@courselit/common-models"; -import { AppDispatch, AppState } from "@courselit/state-management"; +import { + AppDispatch, + AppState, + actionCreators, +} from "@courselit/state-management"; import { BTN_NEW_MAIL, PAGE_HEADER_ALL_MAILS, BROADCASTS, SEQUENCES, BTN_NEW_SEQUENCE, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { FetchBuilder } from "@courselit/utils"; -import { actionCreators } from "@courselit/state-management"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { useRouter } from "next/navigation"; import { ThunkDispatch } from "redux-thunk"; import { @@ -29,6 +31,7 @@ import { CardContent, CardFooter, CardTitle, + useToast, } from "@courselit/components-library"; import { AnyAction } from "redux"; import RequestForm from "./request-form"; @@ -63,6 +66,7 @@ export default function Mails({ >([]); const [siteInfo, setSiteInfo] = useState(); const router = useRouter(); + const { toast } = useToast(); const handleBroadcastPageChange = (newPage: number) => { setBroadcastPage(newPage); @@ -98,7 +102,10 @@ export default function Mails({ setSiteInfo(response.siteInfo); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -138,7 +145,10 @@ export default function Mails({ setBroadcasts(response.broadcasts); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -175,7 +185,10 @@ export default function Mails({ } } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -221,7 +234,10 @@ export default function Mails({ ); } } catch (err) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && (dispatch as ThunkDispatch)( diff --git a/apps/web/components/admin/mails/request-form.tsx b/apps/web/components/admin/mails/request-form.tsx index ee42144e4..49174c505 100644 --- a/apps/web/components/admin/mails/request-form.tsx +++ b/apps/web/components/admin/mails/request-form.tsx @@ -1,12 +1,15 @@ -import { Address, AppMessage } from "@courselit/common-models"; -import { Form, FormField, FormSubmit } from "@courselit/components-library"; -import { AppDispatch } from "@courselit/state-management"; +import { Address } from "@courselit/common-models"; import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; + Form, + FormField, + FormSubmit, + useToast, +} from "@courselit/components-library"; +import { AppDispatch } from "@courselit/state-management"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder, capitalize } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, MAIL_REQUEST_FORM_REASON_FIELD, MAIL_REQUEST_FORM_REASON_PLACEHOLDER, MAIL_REQUEST_FORM_SUBMIT_INITIAL_REQUEST_TEXT, @@ -25,6 +28,7 @@ const RequestForm = ({ address, dispatch, loading }: RequestFormProps) => { const [reason, setReason] = useState(""); const [message, setMessage] = useState(""); const [status, setStatus] = useState(""); + const { toast } = useToast(); useEffect(() => { const loadMailRequestStatus = async () => { @@ -53,7 +57,10 @@ const RequestForm = ({ address, dispatch, loading }: RequestFormProps) => { setStatus(status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -95,13 +102,16 @@ const RequestForm = ({ address, dispatch, loading }: RequestFormProps) => { dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateMailRequest) { - dispatch && - dispatch( - setAppMessage(new AppMessage(MAIL_REQUEST_RECEIVED)), - ); + toast({ + title: "", + description: MAIL_REQUEST_RECEIVED, + }); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/mails/sequence-editor-2.tsx b/apps/web/components/admin/mails/sequence-editor-2.tsx index 16bce46aa..fc83a45b7 100644 --- a/apps/web/components/admin/mails/sequence-editor-2.tsx +++ b/apps/web/components/admin/mails/sequence-editor-2.tsx @@ -1,4 +1,9 @@ -import { Form, FormField, Section } from "@courselit/components-library"; +import { + Form, + FormField, + Section, + useToast, +} from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; import { ChangeEvent, FormEvent, useEffect, useState } from "react"; import { actionCreators } from "@courselit/state-management"; @@ -7,16 +12,12 @@ import { FetchBuilder, getGraphQLQueryStringFromObject, } from "@courselit/utils"; -import { - Address, - AppMessage, - Mail, - UIConstants, -} from "@courselit/common-models"; +import { Address, Mail, UIConstants } from "@courselit/common-models"; import { connect } from "react-redux"; import { BTN_SEND, BTN_SENDING, + ERROR_SNACKBAR_PREFIX, MAIL_BODY_PLACEHOLDER, MAIL_SUBJECT_PLACEHOLDER, MAIL_TO_PLACEHOLDER, @@ -24,7 +25,6 @@ import { PAGE_HEADER_EDIT_MAIL, TOAST_MAIL_SENT, } from "../../../ui-config/strings"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { Breadcrumbs, Link, Button } from "@courselit/components-library"; const { networkAction } = actionCreators; @@ -43,6 +43,7 @@ function MailEditor({ id, address, dispatch }: MailEditorProps) { published: false, }); const [sending, setSending] = useState(false); + const { toast } = useToast(); const debouncedSave = debounce(async () => await saveMail(), 1000); useEffect(() => { @@ -86,7 +87,10 @@ function MailEditor({ id, address, dispatch }: MailEditorProps) { }); } } catch (e: any) { - dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch(networkAction(false)); } @@ -108,7 +112,10 @@ function MailEditor({ id, address, dispatch }: MailEditorProps) { dispatch(networkAction(true)); await fetcher.exec(); } catch (e: any) { - dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch(networkAction(false)); } @@ -145,9 +152,15 @@ function MailEditor({ id, address, dispatch }: MailEditorProps) { if (response.mail) { setMail(response.mail); } - dispatch(setAppMessage(new AppMessage(TOAST_MAIL_SENT))); + toast({ + title: "", + description: TOAST_MAIL_SENT, + }); } catch (e: any) { - dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch(networkAction(false)); setSending(false); diff --git a/apps/web/components/admin/mails/sequence-editor.tsx b/apps/web/components/admin/mails/sequence-editor.tsx index c8e02ec10..16ef7495f 100644 --- a/apps/web/components/admin/mails/sequence-editor.tsx +++ b/apps/web/components/admin/mails/sequence-editor.tsx @@ -1,9 +1,4 @@ -import { - Address, - AppMessage, - Constants, - Course, -} from "@courselit/common-models"; +import { Address, Constants, Course } from "@courselit/common-models"; import { Breadcrumbs, Form, @@ -15,15 +10,13 @@ import { MenuItem, FormSubmit, Skeleton, + useToast, } from "@courselit/components-library"; import { Pause } from "@courselit/icons"; import { Play } from "@courselit/icons"; import { Add, MoreVert } from "@courselit/icons"; import { AppDispatch } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { COMPOSE_SEQUENCE_ENTRANCE_CONDITION, @@ -32,6 +25,7 @@ import { COMPOSE_SEQUENCE_FORM_TITLE, COMPOSE_SEQUENCE_FROM_PLC, DELETE_EMAIL_MENU, + ERROR_SNACKBAR_PREFIX, PAGE_HEADER_ALL_MAILS, PAGE_HEADER_EDIT_SEQUENCE, } from "@ui-config/strings"; @@ -76,6 +70,7 @@ const SequenceEditor = ({ >([]); const [emailsOrder, setEmailsOrder] = useState([]); const [status, setStatus] = useState(null); + const { toast } = useToast(); const onSubmit = async (e: FormEvent, sendLater: boolean = false) => { e.preventDefault(); @@ -136,7 +131,10 @@ const SequenceEditor = ({ setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); // setLoaded(true); @@ -199,7 +197,10 @@ const SequenceEditor = ({ setProducts([...response.courses]); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }, [dispatch, fetch]); @@ -248,7 +249,10 @@ const SequenceEditor = ({ setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); // setLoaded(true); @@ -325,7 +329,10 @@ const SequenceEditor = ({ setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); // setLoaded(true); @@ -426,7 +433,10 @@ const SequenceEditor = ({ setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); // setLoaded(true); @@ -493,7 +503,10 @@ const SequenceEditor = ({ setStatus(sequence.status); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); // setLoaded(true); diff --git a/apps/web/components/admin/mails/sequence-mail-editor.tsx b/apps/web/components/admin/mails/sequence-mail-editor.tsx index 557fb0487..86c9268b7 100644 --- a/apps/web/components/admin/mails/sequence-mail-editor.tsx +++ b/apps/web/components/admin/mails/sequence-mail-editor.tsx @@ -1,4 +1,4 @@ -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Breadcrumbs, Button, @@ -6,16 +6,15 @@ import { FormField, Link, Select, + useToast, } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { BUTTON_SAVE, COMPOSE_SEQUENCE_EDIT_DELAY, + ERROR_SNACKBAR_PREFIX, MAIL_PREVIEW_TITLE, MAIL_SUBJECT_PLACEHOLDER, PAGE_HEADER_ALL_MAILS, @@ -50,7 +49,7 @@ const SequenceMailEditor = ({ const [published, setPublished] = useState<"unpublished" | "published">( null, ); - + const { toast } = useToast(); const fetch = useMemo( () => new FetchBuilder() @@ -106,7 +105,10 @@ const SequenceMailEditor = ({ } } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -196,7 +198,10 @@ const SequenceMailEditor = ({ } } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/mails/sequences-list.tsx b/apps/web/components/admin/mails/sequences-list.tsx index be26353af..d2d7cf873 100644 --- a/apps/web/components/admin/mails/sequences-list.tsx +++ b/apps/web/components/admin/mails/sequences-list.tsx @@ -2,7 +2,6 @@ import { Address, - AppMessage, Constants, Sequence, SequenceType, @@ -14,14 +13,13 @@ import { TableBody, TableHead, TableRow, + useToast, } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder, capitalize } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, MAIL_TABLE_HEADER_STATUS, MAIL_TABLE_HEADER_SUBJECT, } from "@ui-config/strings"; @@ -49,6 +47,7 @@ const SequencesList = ({ const [sequences, setSequences] = useState< Pick[] >([]); + const { toast } = useToast(); const handlePageChange = (newPage: number) => { setPage(newPage); @@ -101,7 +100,10 @@ const SequencesList = ({ setSequences(response.broadcasts); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -129,7 +131,10 @@ const SequencesList = ({ setCount(response.count); } } catch (e: any) { - dispatch && dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/page-editor/index.tsx b/apps/web/components/admin/page-editor/index.tsx index ac68e8ff5..bcb330ba3 100644 --- a/apps/web/components/admin/page-editor/index.tsx +++ b/apps/web/components/admin/page-editor/index.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; import { - AppMessage, Page, SiteInfo, Typeface, @@ -8,10 +7,7 @@ import { } from "@courselit/common-models"; import type { Address, Media, Profile } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { debounce, FetchBuilder, @@ -24,6 +20,7 @@ import { PAGE_TITLE_EDIT_PAGE, EDIT_PAGE_BUTTON_FONTS, EDIT_PAGE_BUTTON_SEO, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { useRouter } from "next/navigation"; import { @@ -36,7 +33,7 @@ import dynamic from "next/dynamic"; import Head from "next/head"; import widgets from "../../../ui-config/widgets"; import { Sync, CheckCircled } from "@courselit/icons"; -import { Button, Skeleton } from "@courselit/components-library"; +import { Button, Skeleton, useToast } from "@courselit/components-library"; import SeoEditor from "./seo-editor"; const EditWidget = dynamic(() => import("./edit-widget")); @@ -93,6 +90,7 @@ export default function PageEditor({ const [primaryFontFamily, setPrimaryFontFamily] = useState("Roboto, sans-serif"); const [loading, setLoading] = useState(false); + const { toast } = useToast(); const router = useRouter(); const debouncedSave = useCallback( @@ -243,7 +241,10 @@ export default function PageEditor({ setDraftTypefaces(response.site.draftTypefaces); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -280,16 +281,17 @@ export default function PageEditor({ } setPage(pageBeingEdited); } else { - dispatch && - dispatch( - setAppMessage( - new AppMessage(`The page does not exist.`), - ), - ); + toast({ + title: "", + description: `The page does not exist.`, + }); router.replace(`${prefix}/pages`); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -487,7 +489,10 @@ export default function PageEditor({ setDraftTypefaces(response.site.draftTypefaces); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/pages/index.tsx b/apps/web/components/admin/pages/index.tsx index 7d1e291d0..3546d93a5 100644 --- a/apps/web/components/admin/pages/index.tsx +++ b/apps/web/components/admin/pages/index.tsx @@ -1,5 +1,10 @@ -import { Address, AppMessage, Page } from "@courselit/common-models"; -import { Menu2, MenuItem, TableBody } from "@courselit/components-library"; +import { Address, Page } from "@courselit/common-models"; +import { + Menu2, + MenuItem, + TableBody, + useToast, +} from "@courselit/components-library"; import { TableRow } from "@courselit/components-library"; import { TableHead } from "@courselit/components-library"; import { Table } from "@courselit/components-library"; @@ -7,16 +12,14 @@ import { Button, Link } from "@courselit/components-library"; import { View } from "@courselit/icons"; import { Edit, MoreVert } from "@courselit/icons"; import { AppDispatch, AppState } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { APP_MESSAGE_PAGE_DELETED, BTN_NEW_PAGE, DELETE_PAGE_POPUP_HEADER, DELETE_PAGE_POPUP_TEXT, + ERROR_SNACKBAR_PREFIX, MANAGE_PAGES_PAGE_HEADING, PAGES_TABLE_HEADER_ACTIONS, PAGES_TABLE_HEADER_NAME, @@ -36,6 +39,7 @@ interface IndexProps { export const Pages = ({ loading, address, dispatch, prefix }: IndexProps) => { const [pages, setPages] = useState([]); + const { toast } = useToast(); useEffect(() => { loadPages(); @@ -64,7 +68,10 @@ export const Pages = ({ loading, address, dispatch, prefix }: IndexProps) => { setPages(response.pages); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -93,9 +100,15 @@ export const Pages = ({ loading, address, dispatch, prefix }: IndexProps) => { setPages([...pages]); } - dispatch(setAppMessage(new AppMessage(APP_MESSAGE_PAGE_DELETED))); + toast({ + title: "", + description: APP_MESSAGE_PAGE_DELETED, + }); } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/pages/new-page.tsx b/apps/web/components/admin/pages/new-page.tsx index 87cbddfba..a044e7a73 100644 --- a/apps/web/components/admin/pages/new-page.tsx +++ b/apps/web/components/admin/pages/new-page.tsx @@ -1,4 +1,4 @@ -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { AppDispatch } from "@courselit/state-management"; import { Form, @@ -20,10 +20,7 @@ import { } from "@ui-config/strings"; import { FormEvent, useEffect, useState } from "react"; import { FetchBuilder, slugify } from "@courselit/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { Info } from "@courselit/icons"; import { useRouter } from "next/navigation"; @@ -72,7 +69,6 @@ const NewPage = ({ ); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); toast({ title: ERROR_SNACKBAR_PREFIX, description: err.message, diff --git a/apps/web/components/admin/products/editor/content/lesson.tsx b/apps/web/components/admin/products/editor/content/lesson.tsx index b0b8b2202..c3075886c 100644 --- a/apps/web/components/admin/products/editor/content/lesson.tsx +++ b/apps/web/components/admin/products/editor/content/lesson.tsx @@ -18,6 +18,7 @@ import { LESSON_CONTENT_EMBED_PLACEHOLDER, BUTTON_SAVING, MANAGE_COURSES_PAGE_HEADING, + ERROR_SNACKBAR_PREFIX, } from "@ui-config/strings"; import { LESSON_TYPE_TEXT, @@ -34,7 +35,7 @@ import { LESSON_TYPE_EMBED, } from "@ui-config/constants"; import { FetchBuilder, capitalize } from "@courselit/utils"; -import { AppMessage, Media, Profile, Quiz } from "@courselit/common-models"; +import { Media, Profile, Quiz } from "@courselit/common-models"; import type { AppDispatch } from "@courselit/state-management"; import type { Lesson, @@ -60,10 +61,11 @@ import { IconButton, Breadcrumbs, Skeleton, + useToast, } from "@courselit/components-library"; import { QuizBuilder } from "./quiz-builder"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface LessonEditorProps { courseId: string; @@ -112,6 +114,7 @@ const LessonEditor = ({ b1: false, }); const course = useCourse(courseId, address, dispatch); + const { toast } = useToast(); useEffect(() => { lessonId && loadLesson(lessonId); @@ -180,7 +183,10 @@ const LessonEditor = ({ } } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -221,7 +227,10 @@ const LessonEditor = ({ setLoading(true); await fetch.exec(); } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); setLoading(false); @@ -261,7 +270,10 @@ const LessonEditor = ({ setLoading(true); await fetch.exec(); } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); setLoading(false); @@ -313,7 +325,10 @@ const LessonEditor = ({ ); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); setLoading(false); @@ -339,19 +354,19 @@ const LessonEditor = ({ const response = await fetch.exec(); if (response.result) { - dispatch && - dispatch( - setAppMessage( - new AppMessage(APP_MESSAGE_LESSON_DELETED), - ), - ); + toast({ + title: "", + description: APP_MESSAGE_LESSON_DELETED, + }); router.replace( `${prefix}/product/${courseId}${prefix === "/dashboard" ? "/content" : "?tab=Content"}`, ); } } catch (err: any) { - dispatch && - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { setLoading(false); } diff --git a/apps/web/components/admin/products/editor/content/lessons-list.tsx b/apps/web/components/admin/products/editor/content/lessons-list.tsx index bad27660d..29b2d6fe8 100644 --- a/apps/web/components/admin/products/editor/content/lessons-list.tsx +++ b/apps/web/components/admin/products/editor/content/lessons-list.tsx @@ -4,11 +4,12 @@ import { BUTTON_NEW_LESSON_TEXT, DELETE_SECTION_HEADER, EDIT_SECTION_HEADER, + ERROR_SNACKBAR_PREFIX, LESSON_GROUP_DELETED, } from "../../../../../ui-config/strings"; import { CourseWithAdminProps } from "../course-hook"; import { Add, MoreVert } from "@courselit/icons"; -import { Lesson, Address, AppMessage, Group } from "@courselit/common-models"; +import { Lesson, Address, Group } from "@courselit/common-models"; import { Section, Link, @@ -29,6 +30,7 @@ interface LessonSectionProps { address: Address; dispatch?: AppDispatch; prefix: string; + toast: (options: { title: string; description: string }) => void; } function LessonItem({ @@ -61,6 +63,7 @@ function LessonSection({ address, dispatch, prefix, + toast, }: LessonSectionProps) { const updateGroup = async (lessonsOrder: string[]) => { const mutation = ` @@ -91,10 +94,10 @@ function LessonSection({ dispatch && dispatch(actionCreators.networkAction(true)); const response = await fetch.exec(); } catch (err: any) { - dispatch && - dispatch( - actionCreators.setAppMessage(new AppMessage(err.message)), - ); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(actionCreators.networkAction(false)); } @@ -208,12 +211,6 @@ export default function LessonsList({ dispatch && dispatch(actionCreators.networkAction(true)); const response = await fetch.exec(); if (response.removeGroup?.courseId) { - dispatch && - dispatch( - actionCreators.setAppMessage( - new AppMessage(LESSON_GROUP_DELETED), - ), - ); toast({ title: "", description: LESSON_GROUP_DELETED, @@ -224,10 +221,10 @@ export default function LessonsList({ ); } } catch (err: any) { - dispatch && - dispatch( - actionCreators.setAppMessage(new AppMessage(err.message)), - ); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(actionCreators.networkAction(false)); } @@ -248,6 +245,7 @@ export default function LessonsList({ address={address} dispatch={dispatch} prefix={prefix} + toast={toast} /> ))}
diff --git a/apps/web/components/admin/products/editor/course-hook.ts b/apps/web/components/admin/products/editor/course-hook.ts index a4c1e7bb6..5f054725e 100644 --- a/apps/web/components/admin/products/editor/course-hook.ts +++ b/apps/web/components/admin/products/editor/course-hook.ts @@ -1,12 +1,11 @@ import { Address } from "@courselit/common-models"; -import { AppMessage, Lesson } from "@courselit/common-models"; +import { Lesson } from "@courselit/common-models"; +import { useToast } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { Course } from "@models/Course"; +import { ERROR_SNACKBAR_PREFIX } from "@ui-config/strings"; import { useCallback, useEffect, useState } from "react"; export type CourseWithAdminProps = Partial< @@ -24,6 +23,7 @@ export default function useCourse( const [course, setCourse] = useState< CourseWithAdminProps | undefined | null >(); + const { toast } = useToast(); const loadCourse = useCallback( async (courseId: string) => { @@ -92,8 +92,10 @@ export default function useCourse( } } catch (err: any) { setCourse(null); - dispatch && - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/products/editor/details.tsx b/apps/web/components/admin/products/editor/details.tsx index 087ee388a..f93d3354a 100644 --- a/apps/web/components/admin/products/editor/details.tsx +++ b/apps/web/components/admin/products/editor/details.tsx @@ -7,18 +7,17 @@ import { Form, FormField, PageBuilderPropertyHeader, + useToast, } from "@courselit/components-library"; import useCourse from "./course-hook"; import { FetchBuilder } from "@courselit/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; -import { Address, AppMessage, Media, Profile } from "@courselit/common-models"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; +import { Address, Media, Profile } from "@courselit/common-models"; import { APP_MESSAGE_COURSE_SAVED, BUTTON_SAVE, COURSE_CONTENT_HEADER, + ERROR_SNACKBAR_PREFIX, FORM_FIELD_FEATURED_IMAGE, } from "../../../../ui-config/strings"; import { AppDispatch } from "@courselit/state-management"; @@ -42,6 +41,7 @@ export default function Details({ const [featuredImage, setFeaturedImage] = useState>({}); const [refresh, setRefresh] = useState(0); const course = useCourse(id, address, dispatch); + const { toast } = useToast(); useEffect(() => { if (course) { @@ -79,13 +79,16 @@ export default function Details({ dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateCourse) { - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_SAVED, + }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -117,13 +120,16 @@ export default function Details({ dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateCourse) { - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_SAVED, + }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/products/editor/pricing.tsx b/apps/web/components/admin/products/editor/pricing.tsx index 254de223a..993b43651 100644 --- a/apps/web/components/admin/products/editor/pricing.tsx +++ b/apps/web/components/admin/products/editor/pricing.tsx @@ -1,11 +1,14 @@ import React, { FormEvent, useEffect, useState } from "react"; -import { Address, AppMessage, SiteInfo } from "@courselit/common-models"; -import { FormField, Form, Button } from "@courselit/components-library"; -import type { AppDispatch } from "@courselit/state-management"; +import { Address, SiteInfo } from "@courselit/common-models"; import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; + FormField, + Form, + Button, + Select, + useToast, +} from "@courselit/components-library"; +import type { AppDispatch } from "@courselit/state-management"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { APP_MESSAGE_COURSE_SAVED, @@ -23,7 +26,6 @@ import { PRICING_PAID_SUBTITLE, } from "../../../../ui-config/strings"; import useCourse from "./course-hook"; -import { Select } from "@courselit/components-library"; import { COURSE_TYPE_DOWNLOAD } from "../../../../ui-config/constants"; interface PricingProps { @@ -44,6 +46,7 @@ export default function Pricing({ const [costType, setCostType] = useState( course?.costType?.toLowerCase() || PRICING_FREE, ); + const { toast } = useToast(); useEffect(() => { if (course) { @@ -75,13 +78,12 @@ export default function Pricing({ dispatch && dispatch(networkAction(true)); const response = await fetch.exec(); if (response.updateCourse) { - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_SAVED, + }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/products/editor/publish.tsx b/apps/web/components/admin/products/editor/publish.tsx index f5f7f0866..d8172b6b8 100644 --- a/apps/web/components/admin/products/editor/publish.tsx +++ b/apps/web/components/admin/products/editor/publish.tsx @@ -1,12 +1,12 @@ import React, { FormEvent, useEffect, useState } from "react"; -import { Form, Button } from "@courselit/components-library"; +import { Form, Button, useToast } from "@courselit/components-library"; import useCourse from "./course-hook"; import { capitalize, FetchBuilder } from "@courselit/utils"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { BTN_PUBLISH, BTN_UNPUBLISH, + ERROR_SNACKBAR_PREFIX, PUBLISH_TAB_STATUS_SUBTITLE, PUBLISH_TAB_STATUS_TITLE, PUBLISH_TAB_VISIBILITY_SUBTITLE, @@ -25,6 +25,7 @@ export default function Publish({ id, address, dispatch }: PublishProps) { const [published, setPublished] = useState(course?.published); const [privacy, setPrivacy] = useState(course?.privacy as string); const [loading, setLoading] = useState(false); + const { toast } = useToast(); useEffect(() => { if (course) { @@ -82,7 +83,10 @@ export default function Publish({ id, address, dispatch }: PublishProps) { return response.course; } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { setLoading(false); } diff --git a/apps/web/components/admin/products/editor/section.tsx b/apps/web/components/admin/products/editor/section.tsx index eaa5d6857..ea066efb0 100644 --- a/apps/web/components/admin/products/editor/section.tsx +++ b/apps/web/components/admin/products/editor/section.tsx @@ -1,9 +1,4 @@ -import { - Address, - AppMessage, - Constants, - DripType, -} from "@courselit/common-models"; +import { Address, Constants, DripType } from "@courselit/common-models"; import { Button, Form, @@ -11,6 +6,7 @@ import { Section, Skeleton, Switch, + useToast, } from "@courselit/components-library"; import { actionCreators, AppDispatch } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; @@ -22,6 +18,7 @@ import { DRIP_SECTION_STATUS, EDIT_SECTION_DRIP, EDIT_SECTION_HEADER, + ERROR_SNACKBAR_PREFIX, LABEL_DRIP_DATE, LABEL_DRIP_DELAY, LABEL_DRIP_EMAIL_SUBJECT, @@ -61,6 +58,7 @@ export default function SectionEditor({ const [emailSubject, setEmailSubject] = useState(""); const router = useRouter(); const course = useCourse(id, address, dispatch); + const { toast } = useToast(); useEffect(() => { if (section && course && course.groups) { @@ -192,10 +190,10 @@ export default function SectionEditor({ ); } } catch (err: any) { - dispatch && - dispatch( - actionCreators.setAppMessage(new AppMessage(err.message)), - ); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(actionCreators.networkAction(false)); } diff --git a/apps/web/components/admin/products/index.tsx b/apps/web/components/admin/products/index.tsx index c38e37116..a5c7bc7d8 100644 --- a/apps/web/components/admin/products/index.tsx +++ b/apps/web/components/admin/products/index.tsx @@ -9,10 +9,10 @@ import { PRODUCTS_TABLE_HEADER_ACTIONS, BTN_NEW_PRODUCT, PRODUCTS_TABLE_HEADER_TYPE, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { FetchBuilder } from "@courselit/utils"; import type { Address, SiteInfo } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { actionCreators } from "@courselit/state-management"; import Product, { CourseDetails } from "./product"; @@ -22,9 +22,10 @@ import { Table, TableHead, TableBody, + useToast, } from "@courselit/components-library"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface IndexProps { dispatch?: AppDispatch; @@ -46,6 +47,7 @@ export const Index = ({ const [searchText, setSearchText] = useState(""); const [searchState, setSearchState] = useState(0); const [endReached, setEndReached] = useState(false); + const { toast } = useToast(); useEffect(() => { loadCreatorCourses(); @@ -116,7 +118,10 @@ export const Index = ({ } } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/products/new-customer.tsx b/apps/web/components/admin/products/new-customer.tsx index aa3a9a044..931ffd9d2 100644 --- a/apps/web/components/admin/products/new-customer.tsx +++ b/apps/web/components/admin/products/new-customer.tsx @@ -1,7 +1,7 @@ "use client"; import React, { useCallback, useEffect, useState } from "react"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Form, FormField, @@ -16,13 +16,11 @@ import { FetchBuilder } from "@courselit/utils"; import { BTN_GO_BACK, BTN_INVITE, + ERROR_SNACKBAR_PREFIX, PRODUCT_TABLE_CONTEXT_MENU_INVITE_A_CUSTOMER, USER_TAGS_SUBHEADER, } from "@/ui-config/strings"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import useCourse from "./editor/course-hook"; interface NewCustomerProps { @@ -108,16 +106,14 @@ export default function NewCustomer({ setEmail(""); setTags([]); const message = `${response.user.email} has been invited.`; - dispatch && dispatch(setAppMessage(new AppMessage(message))); toast({ title: "Success", description: message, }); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); toast({ - title: "Error", + title: ERROR_SNACKBAR_PREFIX, description: err.message, }); } finally { diff --git a/apps/web/components/admin/products/new-product.tsx b/apps/web/components/admin/products/new-product.tsx index 18720b24c..be79030bc 100644 --- a/apps/web/components/admin/products/new-product.tsx +++ b/apps/web/components/admin/products/new-product.tsx @@ -1,5 +1,5 @@ import React, { FormEvent, useState } from "react"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Form, FormField, @@ -7,6 +7,7 @@ import { Link, Button, Breadcrumbs, + useToast, } from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; @@ -20,6 +21,7 @@ import { BTN_CONTINUE, BTN_NEW_PRODUCT, BUTTON_CANCEL_TEXT, + ERROR_SNACKBAR_PREFIX, FORM_NEW_PRODUCT_MENU_COURSE_SUBTITLE, FORM_NEW_PRODUCT_MENU_DOWNLOADS_SUBTITLE, FORM_NEW_PRODUCT_TITLE_PLC, @@ -27,10 +29,7 @@ import { MANAGE_COURSES_PAGE_HEADING, } from "../../../ui-config/strings"; import { capitalize } from "../../../ui-lib/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { usePathname } from "next/navigation"; interface NewProductProps { @@ -50,6 +49,7 @@ export function NewProduct({ const [type, setType] = useState(COURSE_TYPE_COURSE); const router = useRouter(); const path = usePathname(); + const { toast } = useToast(); const createCourse = async (e: FormEvent) => { e.preventDefault(); @@ -81,7 +81,10 @@ export function NewProduct({ ); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/products/product.tsx b/apps/web/components/admin/products/product.tsx index d15b5908a..0d290247d 100644 --- a/apps/web/components/admin/products/product.tsx +++ b/apps/web/components/admin/products/product.tsx @@ -1,9 +1,10 @@ import React from "react"; -import { AppMessage, Course } from "@courselit/common-models"; +import { Course } from "@courselit/common-models"; import { APP_MESSAGE_COURSE_DELETED, DELETE_PRODUCT_POPUP_HEADER, DELETE_PRODUCT_POPUP_TEXT, + ERROR_SNACKBAR_PREFIX, PRODUCT_STATUS_DRAFT, PRODUCT_STATUS_PUBLISHED, PRODUCT_TABLE_CONTEXT_MENU_DELETE_PRODUCT, @@ -15,16 +16,14 @@ import { MoreVert } from "@courselit/icons"; import type { AppDispatch } from "@courselit/state-management"; import type { SiteInfo, Address } from "@courselit/common-models"; import { capitalize, FetchBuilder, formatCurrency } from "@courselit/utils"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { Menu2, MenuItem, Link, Chip, TableRow, + useToast, } from "@courselit/components-library"; import { usePathname } from "next/navigation"; @@ -54,6 +53,7 @@ export default function Product({ }) { const product = details; const path = usePathname(); + const { toast } = useToast(); const deleteProduct = async () => { const query = ` @@ -76,13 +76,16 @@ export default function Product({ onDelete(position); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); - dispatch && - dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_COURSE_DELETED)), - ); + toast({ + title: "", + description: APP_MESSAGE_COURSE_DELETED, + }); } }; diff --git a/apps/web/components/admin/settings/apikey/new.tsx b/apps/web/components/admin/settings/apikey/new.tsx index feac13065..4405ddedb 100644 --- a/apps/web/components/admin/settings/apikey/new.tsx +++ b/apps/web/components/admin/settings/apikey/new.tsx @@ -1,10 +1,11 @@ -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Breadcrumbs, Button, Form, FormField, IconButton, + useToast, } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; @@ -17,13 +18,11 @@ import { APIKEY_NEW_LABEL, BUTTON_CANCEL_TEXT, BUTTON_DONE_TEXT, + ERROR_SNACKBAR_PREFIX, } from "@ui-config/strings"; import Link from "next/link"; import { FormEvent, useState } from "react"; -import { - networkAction, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { networkAction } from "@courselit/state-management/dist/action-creators"; import { Clipboard } from "@courselit/icons"; interface NewApikeyProps { @@ -41,18 +40,17 @@ export default function NewApikey({ }: NewApikeyProps) { const [name, setName] = useState(""); const [apikey, setApikey] = useState(""); + const { toast } = useToast(); const copyApikey = (e: FormEvent) => { e.preventDefault(); if (window.isSecureContext && navigator.clipboard) { navigator.clipboard.writeText(apikey); - dispatch && - dispatch( - setAppMessage( - new AppMessage(APIKEY_NEW_GENERATED_KEY_COPIED), - ), - ); + toast({ + title: "", + description: APIKEY_NEW_GENERATED_KEY_COPIED, + }); } }; @@ -82,7 +80,10 @@ export default function NewApikey({ setApikey(response.apikey.key); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/settings/index.tsx b/apps/web/components/admin/settings/index.tsx index 330adef08..2af124c89 100644 --- a/apps/web/components/admin/settings/index.tsx +++ b/apps/web/components/admin/settings/index.tsx @@ -39,10 +39,11 @@ import { SITE_SETTINGS_RAZORPAY_KEY_TEXT, MEDIA_SELECTOR_UPLOAD_BTN_CAPTION, MEDIA_SELECTOR_REMOVE_BTN_CAPTION, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { FetchBuilder, capitalize } from "@courselit/utils"; import { decode, encode } from "base-64"; -import { AppMessage, Profile, UIConstants } from "@courselit/common-models"; +import { Profile, UIConstants } from "@courselit/common-models"; import type { SiteInfo, Address, Media } from "@courselit/common-models"; import { actionCreators } from "@courselit/state-management"; import currencies from "@/data/currencies.json"; @@ -61,6 +62,7 @@ import { Dialog2, PageBuilderPropertyHeader, Checkbox, + useToast, } from "@courselit/components-library"; import { useRouter } from "next/navigation"; @@ -73,7 +75,7 @@ const { MIMETYPE_IMAGE, } = UIConstants; -const { networkAction, newSiteInfoAvailable, setAppMessage } = actionCreators; +const { networkAction, newSiteInfoAvailable } = actionCreators; interface SettingsProps { siteinfo: SiteInfo; @@ -106,6 +108,7 @@ const Settings = (props: SettingsProps) => { ? props.selectedTab : SITE_SETTINGS_SECTION_GENERAL; const router = useRouter(); + const { toast } = useToast(); const fetch = new FetchBuilder() .setUrl(`${props.address.backend}/api/graph`) @@ -266,12 +269,16 @@ const Settings = (props: SettingsProps) => { const response = await fetchRequest.exec(); if (response.settings.settings) { setSettingsState(response.settings.settings); - props.dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_SETTINGS_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_SETTINGS_SAVED, + }); } } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } @@ -321,12 +328,16 @@ const Settings = (props: SettingsProps) => { const response = await fetchRequest.exec(); if (response.settings.settings) { setSettingsState(response.settings.settings); - props.dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_SETTINGS_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_SETTINGS_SAVED, + }); } } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } @@ -378,12 +389,16 @@ const Settings = (props: SettingsProps) => { const response = await fetchRequest.exec(); if (response.settings.settings) { setSettingsState(response.settings.settings); - props.dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_SETTINGS_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_SETTINGS_SAVED, + }); } } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } @@ -434,12 +449,16 @@ const Settings = (props: SettingsProps) => { const response = await fetchRequest.exec(); if (response.settings.settings) { setSettingsState(response.settings.settings); - props.dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_SETTINGS_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_SETTINGS_SAVED, + }); } } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } @@ -526,12 +545,16 @@ const Settings = (props: SettingsProps) => { const response = await fetchRequest.exec(); if (response.settings.settings) { setSettingsState(response.settings.settings); - props.dispatch( - setAppMessage(new AppMessage(APP_MESSAGE_SETTINGS_SAVED)), - ); + toast({ + title: "", + description: APP_MESSAGE_SETTINGS_SAVED, + }); } } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } @@ -581,7 +604,10 @@ const Settings = (props: SettingsProps) => { ), ); } catch (e: any) { - props.dispatch(setAppMessage(new AppMessage(e.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: e.message, + }); } finally { props.dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/users/details.tsx b/apps/web/components/admin/users/details.tsx index 624424aaf..6be964ab9 100644 --- a/apps/web/components/admin/users/details.tsx +++ b/apps/web/components/admin/users/details.tsx @@ -8,9 +8,9 @@ import { USER_EMAIL_SUBHEADER, USER_NAME_SUBHEADER, USER_TAGS_SUBHEADER, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { FetchBuilder } from "@courselit/utils"; -import { AppMessage } from "@courselit/common-models"; import PermissionsEditor from "./permissions-editor"; import type { Address, UserWithAdminFields } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; @@ -21,10 +21,11 @@ import { Switch, Breadcrumbs, ComboBox, + useToast, } from "@courselit/components-library"; import { useCallback } from "react"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface DetailsProps { userId: string; @@ -36,6 +37,7 @@ const Details = ({ userId, address, dispatch }: DetailsProps) => { const [userData, setUserData] = useState(); const [enrolledCourses, setEnrolledCourses] = useState([]); const [tags, setTags] = useState([]); + const { toast } = useToast(); useEffect(() => { getUserDetails(); @@ -100,7 +102,10 @@ const Details = ({ userId, address, dispatch }: DetailsProps) => { setUserData(response.user); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -160,7 +165,10 @@ const Details = ({ userId, address, dispatch }: DetailsProps) => { setUserData(response.user); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -201,7 +209,10 @@ const Details = ({ userId, address, dispatch }: DetailsProps) => { setUserData(response.user); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/users/filter-container/filter-editor/product.tsx b/apps/web/components/admin/users/filter-container/filter-editor/product.tsx index f201727de..701f183c1 100644 --- a/apps/web/components/admin/users/filter-container/filter-editor/product.tsx +++ b/apps/web/components/admin/users/filter-container/filter-editor/product.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback, useMemo } from "react"; import { FetchBuilder } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, POPUP_CANCEL_ACTION, USER_FILTER_APPLY_BTN, USER_FILTER_CATEGORY_PRODUCT, @@ -15,10 +16,9 @@ import { Form, FormSubmit, Select, + useToast, } from "@courselit/components-library"; -import { Address, AppMessage, Course } from "@courselit/common-models"; -import { actionCreators } from "@courselit/state-management"; -const { setAppMessage } = actionCreators; +import { Address, Course } from "@courselit/common-models"; interface ProductFilterEditorProps { onApply: (...args: any[]) => any; @@ -36,6 +36,7 @@ export default function ProductFilterEditor({ const [products, setProducts] = useState< Pick[] >([]); + const { toast } = useToast(); const loadCreatorCourses = useCallback(async () => { const query = ` @@ -58,7 +59,10 @@ export default function ProductFilterEditor({ setProducts([...response.courses]); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }, [address.backend, dispatch]); diff --git a/apps/web/components/admin/users/filter-container/filter-editor/tagged.tsx b/apps/web/components/admin/users/filter-container/filter-editor/tagged.tsx index fca8b27c2..3d171df61 100644 --- a/apps/web/components/admin/users/filter-container/filter-editor/tagged.tsx +++ b/apps/web/components/admin/users/filter-container/filter-editor/tagged.tsx @@ -1,13 +1,15 @@ -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Button, Form, FormSubmit, Select, + useToast, } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; import { + ERROR_SNACKBAR_PREFIX, POPUP_CANCEL_ACTION, USER_FILTER_APPLY_BTN, USER_FILTER_CATEGORY_TAGGED, @@ -19,9 +21,7 @@ import React, { useState } from "react"; import { useCallback } from "react"; import { useMemo } from "react"; import PopoverHeader from "../popover-header"; -import { actionCreators } from "@courselit/state-management"; import { useEffect } from "react"; -const { setAppMessage } = actionCreators; interface TaggedFilterEditorProps { onApply: (...args: any[]) => any; @@ -37,6 +37,7 @@ export default function TaggedFilterEditor({ const [condition, setCondition] = useState(USER_FILTER_PRODUCT_HAS); const [value, setValue] = useState(""); const [tags, setTags] = useState([]); + const { toast } = useToast(); const getTags = useCallback(async () => { const query = ` @@ -55,7 +56,10 @@ export default function TaggedFilterEditor({ setTags(response.tags); } } catch (err) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } }, [address.backend, dispatch]); diff --git a/apps/web/components/admin/users/filter-container/filter-save.tsx b/apps/web/components/admin/users/filter-container/filter-save.tsx index 9e8944f26..9f999e6ed 100644 --- a/apps/web/components/admin/users/filter-container/filter-save.tsx +++ b/apps/web/components/admin/users/filter-container/filter-save.tsx @@ -1,8 +1,14 @@ import React, { useState, ChangeEvent } from "react"; -import { Form, FormField, FormSubmit } from "@courselit/components-library"; +import { + Form, + FormField, + FormSubmit, + useToast, +} from "@courselit/components-library"; import Segment from "@ui-models/segment"; import { BUTTON_SAVE, + ERROR_SNACKBAR_PREFIX, USER_FILTER_NEW_SEGMENT_NAME, USER_FILTER_SAVE_DESCRIPTION, } from "@ui-config/strings"; @@ -11,7 +17,6 @@ import { FetchBuilder } from "@courselit/utils"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { Address, - AppMessage, UserFilter, UserFilterAggregator, } from "@courselit/common-models"; @@ -20,7 +25,7 @@ import { actionCreators } from "@courselit/state-management"; import type { AnyAction } from "redux"; import PopoverDescription from "./popover-description"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface FilterSaveProps { filters: UserFilter[]; @@ -38,6 +43,7 @@ export default function FilterSave({ dismissPopover, }: FilterSaveProps) { const [name, setName] = useState(""); + const { toast } = useToast(); const onSubmit = async (e: FormEvent) => { e.preventDefault(); @@ -84,7 +90,10 @@ export default function FilterSave({ dismissPopover(); } } catch (err) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && (dispatch as ThunkDispatch)( diff --git a/apps/web/components/admin/users/filter-container/index.tsx b/apps/web/components/admin/users/filter-container/index.tsx index 17d3999a2..b791bd225 100644 --- a/apps/web/components/admin/users/filter-container/index.tsx +++ b/apps/web/components/admin/users/filter-container/index.tsx @@ -6,12 +6,14 @@ import { IconButton, Popover, Select, + useToast, } from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; import { useState } from "react"; import { ThunkDispatch } from "redux-thunk"; import { + ERROR_SNACKBAR_PREFIX, USER_FILTER_AGGREGATOR_ALL, USER_FILTER_AGGREGATOR_ANY, USER_FILTER_AGGREGATOR_HEADER, @@ -25,7 +27,6 @@ import SegmentEditor from "./segment-editor"; import { AnyAction } from "redux"; import { Address, - AppMessage, State, UserFilter, UserFilterAggregator, @@ -39,7 +40,7 @@ import AppLoader from "@components/app-loader"; const FilterChip = dynamic(() => import("./filter-chip")); const FilterSave = dynamic(() => import("./filter-save")); const FilterEditor = dynamic(() => import("./filter-editor")); -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface FilterContainerProps { onChange: ({ @@ -69,6 +70,8 @@ export default function FilterContainer({ const [internalFilters, setInternalFilters] = useState( filter?.filters || [], ); + const { toast } = useToast(); + const defaultSegment: Segment = useMemo( () => ({ name: USER_FILTER_LABEL_DEFAULT, @@ -132,7 +135,10 @@ export default function FilterContainer({ mapSegments(response.segments); } } catch (err) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && (dispatch as ThunkDispatch)( @@ -184,7 +190,10 @@ export default function FilterContainer({ setCount(response.count); } } catch (err) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { setCountLoading(false); } diff --git a/apps/web/components/admin/users/filter-container/segment-editor.tsx b/apps/web/components/admin/users/filter-container/segment-editor.tsx index 45f5de755..7ad256120 100644 --- a/apps/web/components/admin/users/filter-container/segment-editor.tsx +++ b/apps/web/components/admin/users/filter-container/segment-editor.tsx @@ -1,12 +1,17 @@ import type { Address } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; -import { Button, IconButton, ScrollArea } from "@courselit/components-library"; +import { + Button, + IconButton, + ScrollArea, + useToast, +} from "@courselit/components-library"; import { Delete } from "@courselit/icons"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; import React, { useState } from "react"; import type { ThunkDispatch } from "redux-thunk"; import { + ERROR_SNACKBAR_PREFIX, POPUP_CANCEL_ACTION, POPUP_OK_ACTION, USER_DELETE_SEGMENT, @@ -20,7 +25,7 @@ import PopoverHeader from "./popover-header"; import type { AnyAction } from "redux"; import { actionCreators } from "@courselit/state-management"; import DocumentationLink from "@components/public/documentation-link"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface DismissPopoverProps { selectedSegment: string; @@ -44,6 +49,7 @@ export default function SegmentEditor({ dismissPopover, }: SegmentEditorProps) { const [activeSegment, setActiveSegment] = useState(); + const { toast } = useToast(); const deleteSegment = async () => { const mutation = ` @@ -91,7 +97,10 @@ export default function SegmentEditor({ }); } } catch (err) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && (dispatch as ThunkDispatch)( diff --git a/apps/web/components/admin/users/index.tsx b/apps/web/components/admin/users/index.tsx index 7d6f66d96..d225b1be6 100644 --- a/apps/web/components/admin/users/index.tsx +++ b/apps/web/components/admin/users/index.tsx @@ -5,6 +5,7 @@ import { USERS_MANAGER_PAGE_HEADING, USER_TABLE_HEADER_LAST_ACTIVE, BTN_MANAGE_TAGS, + ERROR_SNACKBAR_PREFIX, } from "../../../ui-config/strings"; import { FetchBuilder } from "@courselit/utils"; import { connect } from "react-redux"; @@ -14,7 +15,6 @@ import { User, Address, State, - AppMessage, UserFilter, UserFilterAggregator, } from "@courselit/common-models"; @@ -30,12 +30,13 @@ import { Avatar, AvatarFallback, AvatarImage, + useToast, } from "@courselit/components-library"; import { formattedLocaleDate } from "@ui-lib/utils"; import FilterContainer from "./filter-container"; import { useCallback } from "react"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface UserManagerProps { address: Address; @@ -51,6 +52,7 @@ export const Users = ({ address, dispatch, loading }: UserManagerProps) => { const [filtersAggregator, setFiltersAggregator] = useState("or"); const [count, setCount] = useState(0); + const { toast } = useToast(); /* const handleRowsPerPageChange = ( @@ -149,7 +151,10 @@ export const Users = ({ address, dispatch, loading }: UserManagerProps) => { setCount(response.count); } } catch (err) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { (dispatch as ThunkDispatch)( networkAction(false), diff --git a/apps/web/components/admin/users/permissions-editor.tsx b/apps/web/components/admin/users/permissions-editor.tsx index 51a6a1250..9f4851ac6 100644 --- a/apps/web/components/admin/users/permissions-editor.tsx +++ b/apps/web/components/admin/users/permissions-editor.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import { + ERROR_SNACKBAR_PREFIX, PERM_SECTION_HEADER, USER_PERMISSION_AREA_SUBTEXT, } from "@ui-config/strings"; @@ -7,14 +8,13 @@ import { connect } from "react-redux"; import { FetchBuilder } from "@courselit/utils"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { actionCreators } from "@courselit/state-management"; -import { AppMessage } from "@courselit/common-models"; import type { User, Address } from "@courselit/common-models"; -import { Checkbox } from "@courselit/components-library"; +import { Checkbox, useToast } from "@courselit/components-library"; import { Section } from "@courselit/components-library"; import permissionToCaptionMap from "./permissions-to-caption-map"; import DocumentationLink from "@components/public/documentation-link"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface PermissionsEditorProps { user: User; @@ -30,6 +30,7 @@ export function PermissionsEditor({ networkAction: networkCallUnderway, }: PermissionsEditorProps) { const [activePermissions, setActivePermissions] = useState([]); + const { toast } = useToast(); useEffect(() => { setActivePermissions(user.permissions); @@ -72,10 +73,10 @@ export function PermissionsEditor({ setActivePermissions(response.user.permissions); } } catch (err: any) { - dispatch && - (dispatch as ThunkDispatch)( - setAppMessage(new AppMessage(err.message)), - ); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && (dispatch as ThunkDispatch)( diff --git a/apps/web/components/admin/users/tags/index.tsx b/apps/web/components/admin/users/tags/index.tsx index 3f32423e5..e2520c538 100644 --- a/apps/web/components/admin/users/tags/index.tsx +++ b/apps/web/components/admin/users/tags/index.tsx @@ -8,12 +8,14 @@ import { TableBody, TableHead, TableRow, + useToast, } from "@courselit/components-library"; import { AppDispatch } from "@courselit/state-management"; import { BTN_NEW_TAG, DELETE_TAG_POPUP_DESC, DELETE_TAG_POPUP_HEADER, + ERROR_SNACKBAR_PREFIX, PRODUCTS_TABLE_HEADER_ACTIONS, TAGS_TABLE_CONTEXT_MENU_DELETE_PRODUCT, TAGS_TABLE_CONTEXT_MENU_UNTAG, @@ -28,12 +30,12 @@ import { useCallback } from "react"; import { useEffect } from "react"; import { actionCreators } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { useState } from "react"; import { MoreVert } from "@courselit/icons"; import { usePathname } from "next/navigation"; import clsx from "clsx"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface TagsProps { address: Address; @@ -50,6 +52,7 @@ export default function Tags({ address, dispatch, prefix }: TagsProps) { const [tags, setTags] = useState([]); const [loading, setLoading] = useState(false); const path = usePathname(); + const { toast } = useToast(); const getTags = useCallback(async () => { const query = ` @@ -109,7 +112,10 @@ export default function Tags({ address, dispatch, prefix }: TagsProps) { setTags(response.tags); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } @@ -141,7 +147,10 @@ export default function Tags({ address, dispatch, prefix }: TagsProps) { setTags(response.tags); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch && dispatch(networkAction(false)); } diff --git a/apps/web/components/admin/users/tags/new.tsx b/apps/web/components/admin/users/tags/new.tsx index e8897870d..3e4266030 100644 --- a/apps/web/components/admin/users/tags/new.tsx +++ b/apps/web/components/admin/users/tags/new.tsx @@ -1,17 +1,19 @@ import React, { useState, ChangeEvent } from "react"; -import { Address, AppMessage } from "@courselit/common-models"; +import { Address } from "@courselit/common-models"; import { Breadcrumbs, Button, Form, FormField, Link, + useToast, } from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; import { BTN_CONTINUE, BTN_NEW_TAG, BUTTON_CANCEL_TEXT, + ERROR_SNACKBAR_PREFIX, USERS_MANAGER_PAGE_HEADING, USERS_TAG_HEADER, } from "@ui-config/strings"; @@ -20,7 +22,7 @@ import { FetchBuilder } from "@courselit/utils"; import { actionCreators } from "@courselit/state-management"; import { FormEvent } from "react"; import { usePathname, useRouter } from "next/navigation"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface NewTagProps { address: Address; @@ -32,6 +34,7 @@ export function NewTag({ address, dispatch }: NewTagProps) { const [loading, setLoading] = useState(false); const router = useRouter(); const path = usePathname(); + const { toast } = useToast(); const createTag = async (e: FormEvent) => { e.preventDefault(); @@ -65,7 +68,10 @@ export function NewTag({ address, dispatch }: NewTagProps) { ); } } catch (err: any) { - dispatch && dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { setLoading(false); dispatch && dispatch(networkAction(false)); diff --git a/apps/web/components/app-toast.tsx b/apps/web/components/app-toast.tsx deleted file mode 100644 index aa74260b9..000000000 --- a/apps/web/components/app-toast.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import { actionCreators } from "@courselit/state-management"; -import type { AppDispatch, AppState } from "@courselit/state-management"; -import { Toast } from "@courselit/components-library"; -import { Message } from "@courselit/common-models"; - -const { clearAppMessage } = actionCreators; - -interface AppToastProps { - message: Message; - dispatch: any; -} - -export const AppToast = (props: AppToastProps) => { - const { message, dispatch } = props; - - /* - const handleClose: any = (_: Event | SyntheticEvent, reason: string) => { - if (reason === "clickaway") { - return; - } - - props.dispatch(clearAppMessage()); - }; - - const getActionButtonsArray = () => { - const actionButtonsArray = [ - - - , - ]; - if (action) { - actionButtonsArray.unshift( - , - ); - } - - return actionButtonsArray; - }; - */ - return ( - - ); -}; - -const mapStateToProps = (state: AppState) => ({ - message: state.message, -}); - -const mapDispatchToProps = (dispatch: AppDispatch) => ({ - dispatch: dispatch, -}); - -export default connect(mapStateToProps, mapDispatchToProps)(AppToast); diff --git a/apps/web/components/public/base-layout/template/index.tsx b/apps/web/components/public/base-layout/template/index.tsx index a4f626b69..0c1bf4f10 100644 --- a/apps/web/components/public/base-layout/template/index.tsx +++ b/apps/web/components/public/base-layout/template/index.tsx @@ -1,10 +1,9 @@ import React, { ReactNode } from "react"; import WidgetByName from "./widget-by-name"; -import { AppToast } from "../../../app-toast"; import { WidgetInstance } from "@courselit/common-models"; import { Footer, Header } from "@courselit/common-widgets"; import { ArrowDownward, ArrowUpward } from "@courselit/icons"; -import { Button } from "@courselit/components-library"; +import { Button, Toaster } from "@courselit/components-library"; import { AppDispatch, AppState } from "@courselit/state-management"; interface TemplateProps { @@ -206,9 +205,7 @@ const Template = (props: TemplateProps) => { state={state} /> )} - {state.message && dispatch && ( - - )} +
); }; diff --git a/apps/web/components/public/checkout/free.tsx b/apps/web/components/public/checkout/free.tsx index 44c903bda..7817a3411 100644 --- a/apps/web/components/public/checkout/free.tsx +++ b/apps/web/components/public/checkout/free.tsx @@ -1,16 +1,18 @@ import React, { useState } from "react"; -import { ENROLL_BUTTON_TEXT } from "../../../ui-config/strings"; +import { + ENROLL_BUTTON_TEXT, + ERROR_SNACKBAR_PREFIX, +} from "../../../ui-config/strings"; import { connect } from "react-redux"; import { useRouter } from "next/router"; import { actionCreators } from "@courselit/state-management"; import type { Address, Course } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; import { refreshUserProfile } from "@courselit/state-management/dist/action-creators"; -import { Button2 } from "@courselit/components-library"; +import { Button2, useToast } from "@courselit/components-library"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface FreeProps { course: Course; @@ -21,6 +23,7 @@ interface FreeProps { const Free = ({ course, dispatch, address }: FreeProps) => { const router = useRouter(); const [disabled, setDisabled] = useState(false); + const { toast } = useToast(); const handleClick = async () => { const payload = { @@ -46,10 +49,16 @@ const Free = ({ course, dispatch, address }: FreeProps) => { dispatch(refreshUserProfile()); router.replace(`/my-content`); } else if (response.status === "failed") { - dispatch(setAppMessage(new AppMessage(response.error))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: response.error, + }); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); setDisabled(false); diff --git a/apps/web/components/public/checkout/razorpay.tsx b/apps/web/components/public/checkout/razorpay.tsx index 19766abc4..b93e9eeb9 100644 --- a/apps/web/components/public/checkout/razorpay.tsx +++ b/apps/web/components/public/checkout/razorpay.tsx @@ -1,21 +1,18 @@ import React from "react"; -import { Button2 } from "@courselit/components-library"; -import { ENROLL_BUTTON_TEXT } from "../../../ui-config/strings"; +import { Button2, useToast } from "@courselit/components-library"; +import { + ENROLL_BUTTON_TEXT, + ERROR_SNACKBAR_PREFIX, +} from "../../../ui-config/strings"; import { connect } from "react-redux"; import { useRouter } from "next/router"; import type { AppState, AppDispatch } from "@courselit/state-management"; -import { - Address, - AppMessage, - Course, - Profile, - SiteInfo, -} from "@courselit/common-models"; +import { Address, Course, Profile, SiteInfo } from "@courselit/common-models"; import { FetchBuilder } from "@courselit/utils"; import { actionCreators } from "@courselit/state-management"; import Script from "next/script"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface RazorpayProps { course: Course; @@ -28,6 +25,7 @@ interface RazorpayProps { const RazorpayComp = (props: RazorpayProps) => { const { course, siteInfo, address, dispatch, profile } = props; const router = useRouter(); + const { toast } = useToast(); const verifySignature = async (response) => { const fetch = new FetchBuilder() @@ -54,7 +52,10 @@ const RazorpayComp = (props: RazorpayProps) => { `/checkout/${course.courseId}?id=${verifyResponse.purchaseId}&source=/course/${course.slug}/${course.courseId}`, ); } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -102,7 +103,10 @@ const RazorpayComp = (props: RazorpayProps) => { router.replace(`/course/${course.slug}/${course.courseId}`); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/components/public/checkout/stripe.tsx b/apps/web/components/public/checkout/stripe.tsx index 856940e75..ff804cde8 100644 --- a/apps/web/components/public/checkout/stripe.tsx +++ b/apps/web/components/public/checkout/stripe.tsx @@ -1,20 +1,18 @@ import React from "react"; -import { Button2 } from "@courselit/components-library"; +import { Button2, useToast } from "@courselit/components-library"; import { loadStripe } from "@stripe/stripe-js"; -import { ENROLL_BUTTON_TEXT } from "../../../ui-config/strings"; +import { + ENROLL_BUTTON_TEXT, + ERROR_SNACKBAR_PREFIX, +} from "../../../ui-config/strings"; import { connect } from "react-redux"; import { useRouter } from "next/router"; import type { AppState, AppDispatch } from "@courselit/state-management"; -import { - Address, - AppMessage, - Course, - SiteInfo, -} from "@courselit/common-models"; +import { Address, Course, SiteInfo } from "@courselit/common-models"; import { FetchBuilder } from "@courselit/utils"; import { actionCreators } from "@courselit/state-management"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; interface StripeProps { course: Course; @@ -27,6 +25,7 @@ const Stripe = (props: StripeProps) => { const { course, siteInfo, address, dispatch } = props; const stripePromise = loadStripe(siteInfo.stripeKey as string); const router = useRouter(); + const { toast } = useToast(); const handleClick = async () => { const payload = { @@ -60,7 +59,10 @@ const Stripe = (props: StripeProps) => { router.replace(`/course/${course.slug}/${course.courseId}`); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -90,4 +92,4 @@ const mapStateToProps = (state: AppState) => ({ const mapDispatchToProps = (dispatch: AppDispatch) => ({ dispatch }); -export default connect(mapStateToProps)(Stripe); +export default connect(mapStateToProps, mapDispatchToProps)(Stripe); diff --git a/apps/web/components/public/lesson-viewer/index.tsx b/apps/web/components/public/lesson-viewer/index.tsx index 1d7e65f30..3a1a76c60 100644 --- a/apps/web/components/public/lesson-viewer/index.tsx +++ b/apps/web/components/public/lesson-viewer/index.tsx @@ -17,6 +17,7 @@ import { COURSE_PROGRESS_NEXT, COURSE_PROGRESS_PREV, ENROLL_BUTTON_TEXT, + ERROR_SNACKBAR_PREFIX, NOT_ENROLLED_HEADER, } from "../../../ui-config/strings"; import { @@ -24,6 +25,7 @@ import { Link, Button2, Skeleton, + useToast, } from "@courselit/components-library"; import type { Address, @@ -32,13 +34,9 @@ import type { Quiz, SiteInfo, } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import type { AppDispatch, AppState } from "@courselit/state-management"; import { useRouter } from "next/router"; -import { - refreshUserProfile, - setAppMessage, -} from "@courselit/state-management/dist/action-creators"; +import { refreshUserProfile } from "@courselit/state-management/dist/action-creators"; import { ArrowLeft, ArrowRight, ArrowDownward } from "@courselit/icons"; import { isEnrolled } from "../../../ui-lib/utils"; import LessonEmbedViewer from "./embed-viewer"; @@ -86,6 +84,7 @@ const LessonViewer = ({ const [lesson, setLesson] = useState(); const [error, setError] = useState(); const router = useRouter(); + const { toast } = useToast(); useEffect(() => { if (status === "authenticated") { @@ -176,7 +175,10 @@ const LessonViewer = ({ } } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/components/public/lesson-viewer/quiz-viewer.tsx b/apps/web/components/public/lesson-viewer/quiz-viewer.tsx index ef69a459d..3c46759fe 100644 --- a/apps/web/components/public/lesson-viewer/quiz-viewer.tsx +++ b/apps/web/components/public/lesson-viewer/quiz-viewer.tsx @@ -1,6 +1,5 @@ import { Address, - AppMessage, Question, Quiz as QuizViewer, } from "@courselit/common-models"; @@ -9,17 +8,17 @@ import { AppDispatch, AppState, } from "@courselit/state-management"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { ChangeEvent, useState } from "react"; import { connect } from "react-redux"; import { + ERROR_SNACKBAR_PREFIX, QUIZ_FAIL_MESSAGE, QUIZ_PASS_MESSAGE, QUIZ_VIEWER_EVALUATE_BTN, QUIZ_VIEWER_EVALUATE_BTN_LOADING, } from "../../../ui-config/strings"; -import { Form, FormSubmit } from "@courselit/components-library"; +import { Form, FormSubmit, useToast } from "@courselit/components-library"; const { networkAction } = actionCreators; @@ -36,6 +35,7 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) { ...content.questions.map((item) => []), ]); const [loading, setLoading] = useState(false); + const { toast } = useToast(); const setAnswerForQuestion = ( checked: boolean, @@ -98,25 +98,22 @@ function QuizViewer({ content, lessonId, dispatch, address }: QuizViewerProps) { if (response.result) { const { pass, score, passingGrade } = response.result; if (pass) { - dispatch( - setAppMessage( - new AppMessage( - `${QUIZ_PASS_MESSAGE} ${score} points.`, - ), - ), - ); + toast({ + title: "", + description: `${QUIZ_PASS_MESSAGE} ${score} points.`, + }); } else { - dispatch( - setAppMessage( - new AppMessage( - `${QUIZ_FAIL_MESSAGE} ${score} points. Requires ${passingGrade} points.`, - ), - ), - ); + toast({ + title: "", + description: `${QUIZ_FAIL_MESSAGE} ${score} points. Requires ${passingGrade} points.`, + }); } } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); setLoading(false); diff --git a/apps/web/components/public/purchase-status.tsx b/apps/web/components/public/purchase-status.tsx index 17be3f1b6..000a428f0 100644 --- a/apps/web/components/public/purchase-status.tsx +++ b/apps/web/components/public/purchase-status.tsx @@ -15,16 +15,17 @@ import { VERIFY_PAYMENT_BUTTON, VISIT_COURSE_BUTTON, PURCHASE_ID_HEADER, + ERROR_SNACKBAR_PREFIX, } from "../../ui-config/strings"; import dynamic from "next/dynamic"; import type { AppDispatch, AppState } from "@courselit/state-management"; -import { Address, AppMessage, Auth } from "@courselit/common-models"; +import { Address, Auth } from "@courselit/common-models"; import { FetchBuilder } from "@courselit/utils"; import { actionCreators } from "@courselit/state-management"; -import { Button, Button2 } from "@courselit/components-library"; +import { Button, Button2, useToast } from "@courselit/components-library"; import Link from "next/link"; -const { networkAction, setAppMessage } = actionCreators; +const { networkAction } = actionCreators; const AppLoader = dynamic(() => import("../app-loader")); @@ -42,6 +43,7 @@ const PurchaseStatus = (props: PurchaseStatusProps) => { const { id, source } = router.query; const courseLink: string = (source as string) || ""; const { dispatch, address } = props; + const { toast } = useToast(); useEffect(() => { if (props.auth.checked && props.auth.guest) { @@ -69,7 +71,10 @@ const PurchaseStatus = (props: PurchaseStatusProps) => { const response = await fetch.exec(); setStatus(response.status); } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/components/public/scaffold.tsx b/apps/web/components/public/scaffold.tsx index ab7685651..b445ac8df 100644 --- a/apps/web/components/public/scaffold.tsx +++ b/apps/web/components/public/scaffold.tsx @@ -3,8 +3,7 @@ import Header from "./base-layout/header"; import { connect } from "react-redux"; import { useRouter } from "next/router"; import { AppDispatch, AppState } from "@courselit/state-management"; -import { Chip, Link, Modal } from "@courselit/components-library"; -import { AppToast } from "@components/app-toast"; +import { Chip, Link, Modal, Toaster } from "@courselit/components-library"; import { Message, SiteInfo } from "@courselit/common-models"; export interface ComponentScaffoldMenuItem { @@ -125,9 +124,7 @@ const ComponentScaffold = ({ > {drawer} - {message && dispatch && ( - - )} + ); }; diff --git a/apps/web/pages/dashboard/page/[id]/edit.tsx b/apps/web/pages/dashboard/page/[id]/edit.tsx index 26568bc2d..0d22636b2 100644 --- a/apps/web/pages/dashboard/page/[id]/edit.tsx +++ b/apps/web/pages/dashboard/page/[id]/edit.tsx @@ -18,7 +18,7 @@ import { import { canAccessDashboard } from "@ui-lib/utils"; import { useSession } from "next-auth/react"; import AppLoader from "@components/app-loader"; -import { AppToast } from "@components/app-toast"; +import { Toaster } from "@courselit/components-library"; interface EditPageProps { address: Address; @@ -83,9 +83,7 @@ function EditPage({ prefix="/dashboard" state={state} /> - {message && dispatch && ( - - )} + ); } diff --git a/apps/web/pages/login.tsx b/apps/web/pages/login.tsx index 9a7c35566..a30b63bf5 100644 --- a/apps/web/pages/login.tsx +++ b/apps/web/pages/login.tsx @@ -10,18 +10,22 @@ import { LOGIN_FORM_DISCLAIMER, LOADING, LOGIN_SUCCESS, + ERROR_SNACKBAR_PREFIX, } from "../ui-config/strings"; import { useRouter } from "next/router"; import type { Address, Auth, State } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import { connect } from "react-redux"; import { AppDispatch } from "@courselit/state-management"; import BaseLayout from "../components/public/base-layout"; import { getBackendAddress, getPage } from "../ui-lib/utils"; -import { Form, FormField, FormSubmit } from "@courselit/components-library"; +import { + Form, + FormField, + FormSubmit, + useToast, +} from "@courselit/components-library"; import { signIn } from "next-auth/react"; import { FormEvent } from "react"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import Link from "next/link"; import { useEffect } from "react"; @@ -44,6 +48,7 @@ const Login = ({ page, auth, dispatch }: LoginProps) => { const router = useRouter(); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); + const { toast } = useToast(); useEffect(() => { if (!auth.guest) { @@ -75,7 +80,10 @@ const Login = ({ page, auth, dispatch }: LoginProps) => { if (response.ok) { setShowCode(true); } else { - dispatch(setAppMessage(new AppMessage(resp.error))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: resp.error, + }); } } finally { setLoading(false); @@ -94,7 +102,10 @@ const Login = ({ page, auth, dispatch }: LoginProps) => { if (response?.error) { setError(`Can't sign you in at this time`); } else { - dispatch(setAppMessage(new AppMessage(LOGIN_SUCCESS))); + toast({ + title: "", + description: LOGIN_SUCCESS, + }); } } finally { setLoading(false); diff --git a/apps/web/pages/profile/index.tsx b/apps/web/pages/profile/index.tsx index 0fcc83015..018239704 100644 --- a/apps/web/pages/profile/index.tsx +++ b/apps/web/pages/profile/index.tsx @@ -13,6 +13,7 @@ import { PROFILE_SECTION_DISPLAY_PICTURE, MEDIA_SELECTOR_UPLOAD_BTN_CAPTION, MEDIA_SELECTOR_REMOVE_BTN_CAPTION, + ERROR_SNACKBAR_PREFIX, } from "../../ui-config/strings"; import { connect } from "react-redux"; import { actionCreators } from "@courselit/state-management"; @@ -29,6 +30,7 @@ import { Avatar, AvatarFallback, AvatarImage, + useToast, } from "@courselit/components-library"; import type { Address, @@ -38,7 +40,6 @@ import type { Profile, State, } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import { AppDispatch } from "@courselit/state-management"; import BaseLayout from "../../components/public/base-layout"; import { useRouter } from "next/router"; @@ -67,8 +68,9 @@ function ProfileIndex({ useState>(); const [avatar, setAvatar] = useState>({}); const [subscribedToUpdates, setSubscribedToUpdates] = useState(false); - const { networkAction, refreshUserProfile, setAppMessage } = actionCreators; + const { networkAction, refreshUserProfile } = actionCreators; const router = useRouter(); + const { toast } = useToast(); useEffect(() => { if (auth.checked && auth.guest) { @@ -162,9 +164,15 @@ function ProfileIndex({ dispatch(networkAction(true)); await fetch.exec(); dispatch(refreshUserProfile()); - dispatch(setAppMessage(new AppMessage(APP_MESSAGE_CHANGES_SAVED))); + toast({ + title: "", + description: APP_MESSAGE_CHANGES_SAVED, + }); } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -213,9 +221,15 @@ function ProfileIndex({ dispatch(networkAction(true)); await fetch.exec(); dispatch(refreshUserProfile()); - dispatch(setAppMessage(new AppMessage(APP_MESSAGE_CHANGES_SAVED))); + toast({ + title: "", + description: APP_MESSAGE_CHANGES_SAVED, + }); } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } @@ -250,7 +264,10 @@ function ProfileIndex({ await fetch.exec(); } catch (err: any) { setSubscribedToUpdates(!state); - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(networkAction(false)); } diff --git a/apps/web/ui-models/app-message.ts b/apps/web/ui-models/app-message.ts deleted file mode 100644 index 428594863..000000000 --- a/apps/web/ui-models/app-message.ts +++ /dev/null @@ -1,32 +0,0 @@ -interface AppMessageAction { - text: string; - cb: (...args: any[]) => void; -} - -class AppMessage { - public action?: AppMessageAction; - - constructor( - private message: string, - private actionText?: string, - private actionFunc?: (...args: any[]) => void, - ) { - if (actionText && typeof actionText !== "string") { - throw new Error("actionText should be of type string"); - } - - if (actionFunc && typeof actionFunc !== "function") { - throw new Error("actionText should be of type function"); - } - - this.message = message; - if (actionText && actionFunc) { - this.action = { - text: actionText, - cb: actionFunc, - }; - } - } -} - -export default AppMessage; diff --git a/packages/common-models/src/app-message.ts b/packages/common-models/src/app-message.ts deleted file mode 100644 index 222ab09ff..000000000 --- a/packages/common-models/src/app-message.ts +++ /dev/null @@ -1,32 +0,0 @@ -interface AppMessageAction { - text: string; - cb: (...args: unknown[]) => void; -} - -class AppMessage { - public action?: AppMessageAction; - - constructor( - private message: string, - private actionText?: string, - private actionFunc?: (...args: unknown[]) => void, - ) { - if (actionText && typeof actionText !== "string") { - throw new Error("actionText should be of type string"); - } - - if (actionFunc && typeof actionFunc !== "function") { - throw new Error("actionText should be of type function"); - } - - this.message = message; - if (actionText && actionFunc) { - this.action = { - text: actionText, - cb: actionFunc, - }; - } - } -} - -export default AppMessage; diff --git a/packages/common-models/src/index.ts b/packages/common-models/src/index.ts index 9fdb6e210..ac89b5697 100644 --- a/packages/common-models/src/index.ts +++ b/packages/common-models/src/index.ts @@ -1,4 +1,3 @@ -export { default as AppMessage } from "./app-message"; export { default as Message } from "./message"; export type { default as Address } from "./address"; export type { default as Auth } from "./auth"; diff --git a/packages/common-widgets/src/banner/widget.tsx b/packages/common-widgets/src/banner/widget.tsx index 7d6bf007f..7451fa4f6 100644 --- a/packages/common-widgets/src/banner/widget.tsx +++ b/packages/common-widgets/src/banner/widget.tsx @@ -1,7 +1,7 @@ "use client"; import { FormEvent, useState } from "react"; -import { AppMessage, Media, WidgetProps } from "@courselit/common-models"; +import { Media, WidgetProps } from "@courselit/common-models"; import { Image, PriceTag, @@ -10,9 +10,9 @@ import { FormField, Button2, Link, + useToast, } from "@courselit/components-library"; import { actionCreators } from "@courselit/state-management"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { FetchBuilder } from "@courselit/utils"; import { DEFAULT_FAILURE_MESSAGE, DEFAULT_SUCCESS_MESSAGE } from "./constants"; import Settings from "./settings"; @@ -40,6 +40,7 @@ export default function Widget({ }: WidgetProps) { const [email, setEmail] = useState(""); const [success, setSuccess] = useState(false); + const { toast } = useToast(); const type = Object.keys(product).length === 0 ? "site" : "product"; const defaultSuccessMessage: Record = { type: "doc", @@ -128,11 +129,10 @@ export default function Widget({ setSuccess(true); } } catch (e) { - dispatch( - setAppMessage( - new AppMessage(failureMessage || DEFAULT_FAILURE_MESSAGE), - ), - ); + toast({ + title: "", + description: failureMessage || DEFAULT_FAILURE_MESSAGE, + }); } finally { dispatch(actionCreators.networkAction(false)); } diff --git a/packages/common-widgets/src/content/widget.tsx b/packages/common-widgets/src/content/widget.tsx index 1dfbad17a..8fb95f2a0 100644 --- a/packages/common-widgets/src/content/widget.tsx +++ b/packages/common-widgets/src/content/widget.tsx @@ -1,8 +1,7 @@ "use client"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { - AppMessage, Course, Group, Lesson, @@ -12,23 +11,24 @@ import { import Settings from "./settings"; import { FetchBuilder } from "@courselit/utils"; import { actionCreators } from "@courselit/state-management"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { Link, Chip, LessonIcon, TextRenderer, Skeleton, + useToast, + Badge, + Accordion, + AccordionItem, + AccordionTrigger, + AccordionContent, } from "@courselit/components-library"; import { verticalPadding as defaultVerticalPadding, horizontalPadding as defaultHorizontalPadding, } from "./defaults"; -import { Badge } from "@courselit/components-library"; -import { Accordion } from "@courselit/components-library"; -import { AccordionItem } from "@courselit/components-library"; -import { AccordionTrigger } from "@courselit/components-library"; -import { AccordionContent } from "@courselit/components-library"; +import { ERROR_SNACKBAR_PREFIX } from "../../../../apps/web/ui-config/strings"; interface CourseWithGroups extends Course { groups: Group[]; @@ -56,6 +56,7 @@ export default function Widget({ const [formattedCourse, setFormattedCourse] = useState< Record >({}); + const { toast } = useToast(); useEffect(() => { if (product.courseId) { @@ -116,7 +117,10 @@ export default function Widget({ setCourse(response.course); } } catch (err: any) { - dispatch(setAppMessage(new AppMessage(err.message))); + toast({ + title: ERROR_SNACKBAR_PREFIX, + description: err.message, + }); } finally { dispatch(actionCreators.networkAction(false)); } diff --git a/packages/common-widgets/src/email-form/widget.tsx b/packages/common-widgets/src/email-form/widget.tsx index 9f8a34667..4b4c0451c 100644 --- a/packages/common-widgets/src/email-form/widget.tsx +++ b/packages/common-widgets/src/email-form/widget.tsx @@ -1,18 +1,22 @@ "use client"; -import React, { FormEvent, useState, useEffect } from "react"; -import { AppMessage, WidgetProps } from "@courselit/common-models"; +import { FormEvent, useState, useEffect } from "react"; +import { WidgetProps } from "@courselit/common-models"; import Settings from "./settings"; import { actionCreators } from "@courselit/state-management"; import { FetchBuilder } from "@courselit/utils"; -import { setAppMessage } from "@courselit/state-management/dist/action-creators"; import { DEFAULT_BTN_TEXT, DEFAULT_FAILURE_MESSAGE, DEFAULT_SUCCESS_MESSAGE, DEFAULT_TITLE, } from "./constants"; -import { Form, FormField, Button2 } from "@courselit/components-library"; +import { + Form, + FormField, + Button2, + useToast, +} from "@courselit/components-library"; import { verticalPadding as defaultVerticalPadding, horizontalPadding as defaultHorizontalPadding, @@ -45,6 +49,7 @@ const Widget = ({ const [turnstileToken, setTurnstileToken] = useState(""); const [errorMessage, setErrorMessage] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); + const { toast } = useToast(); const justifyContent = alignment === "center" @@ -99,23 +104,17 @@ const Widget = ({ dispatch(actionCreators.networkAction(true)); const response = await fetch.exec(); if (response.response) { - dispatch( - setAppMessage( - new AppMessage( - successMessage || DEFAULT_SUCCESS_MESSAGE, - ), - ), - ); + toast({ + title: "", + description: successMessage || DEFAULT_SUCCESS_MESSAGE, + }); setName(""); setEmail(""); } else { - dispatch( - setAppMessage( - new AppMessage( - failureMessage || DEFAULT_FAILURE_MESSAGE, - ), - ), - ); + toast({ + title: "", + description: failureMessage || DEFAULT_FAILURE_MESSAGE, + }); } } catch (e) { console.error(e.message); diff --git a/packages/state-management/src/action-creators.ts b/packages/state-management/src/action-creators.ts index d10f65056..7722c387f 100644 --- a/packages/state-management/src/action-creators.ts +++ b/packages/state-management/src/action-creators.ts @@ -9,8 +9,6 @@ import { PROFILE_CLEAR, SITEINFO_AVAILABLE, AUTH_CHECKED, - SET_MESSAGE, - CLEAR_MESSAGE, THEME_AVAILABLE, SET_ADDRESS, TYPEFACES_AVAILABLE, @@ -25,7 +23,6 @@ import type { Theme, Typeface, } from "@courselit/common-models"; -import { AppMessage } from "@courselit/common-models"; import { ThunkAction } from "redux-thunk"; import { AnyAction } from "redux"; import { ServerConfig } from "@courselit/common-models"; @@ -201,14 +198,6 @@ export function newSiteInfoAvailable(info: SiteInfo) { return { type: SITEINFO_AVAILABLE, siteinfo: info }; } -export function setAppMessage(message: AppMessage) { - return (dispatch: any) => dispatch({ type: SET_MESSAGE, message }); -} - -export function clearAppMessage() { - return (dispatch: any) => dispatch({ type: CLEAR_MESSAGE }); -} - export function themeAvailable(theme: Theme) { return { type: THEME_AVAILABLE, theme }; }