diff --git a/.changeset/major-chefs-cough.md b/.changeset/major-chefs-cough.md new file mode 100644 index 000000000..cf9521ff3 --- /dev/null +++ b/.changeset/major-chefs-cough.md @@ -0,0 +1,14 @@ +--- +'@o2s/blocks.notification-details': minor +'@o2s/blocks.notification-list': minor +'@o2s/blocks.article-search': minor +'@o2s/blocks.orders-summary': minor +'@o2s/blocks.order-details': minor +'@o2s/blocks.invoice-list': minor +'@o2s/blocks.service-list': minor +'@o2s/blocks.ticket-list': minor +'@o2s/blocks.order-list': minor +'@o2s/blocks.category': minor +--- + +improved error handling diff --git a/packages/blocks/article-search/src/frontend/ArticleSearch.client.tsx b/packages/blocks/article-search/src/frontend/ArticleSearch.client.tsx index 9200d848e..508a33448 100644 --- a/packages/blocks/article-search/src/frontend/ArticleSearch.client.tsx +++ b/packages/blocks/article-search/src/frontend/ArticleSearch.client.tsx @@ -4,6 +4,10 @@ import { createNavigation } from 'next-intl/navigation'; import React, { useState, useTransition } from 'react'; import { debounce } from 'throttle-debounce'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { Autocomplete } from '@o2s/ui/components/Autocomplete'; import { Container } from '@o2s/ui/components/Container'; @@ -25,18 +29,33 @@ export const ArticleSearchPure: React.FC = ({ }) => { const { useRouter } = createNavigation(routing); const router = useRouter(); + const { labels } = useGlobalContext(); const [suggestions, setSuggestions] = useState([]); const [isPending, startTransition] = useTransition(); const getSuggestions = debounce(300, async (value: string) => { startTransition(async () => { - const result = await sdk.blocks.searchArticles( - { query: value, limit: 5, offset: 0, category }, - { 'x-locale': locale }, - accessToken, - ); - if (result.articles) setSuggestions(result.articles); + try { + const result = await sdk.blocks.searchArticles( + { query: value, limit: 5, offset: 0, category }, + { 'x-locale': locale }, + accessToken, + ); + if (result.articles) { + setSuggestions(result.articles); + } else { + setSuggestions([]); + } + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + duration: 60000, + }); + setSuggestions([]); + } }); }); diff --git a/packages/blocks/article-search/src/frontend/ArticleSearch.server.tsx b/packages/blocks/article-search/src/frontend/ArticleSearch.server.tsx index e1bf9eeeb..604e67277 100644 --- a/packages/blocks/article-search/src/frontend/ArticleSearch.server.tsx +++ b/packages/blocks/article-search/src/frontend/ArticleSearch.server.tsx @@ -24,6 +24,7 @@ export const ArticleSearch: React.FC = async ({ { 'x-locale': locale }, accessToken, ); + return ( = ({ ...component }) => { const { Link: LinkComponent } = createNavigation(routing); + const { labels } = useGlobalContext(); const initialArticles: Request.GetCategoryBlockArticlesQuery = { id: component.id, @@ -42,11 +47,19 @@ export const CategoryPure: React.FC = ({ const handlePagination = (data: Partial) => { startTransition(async () => { - const newArticles = { ...articles, ...data }; - const newData = await sdk.blocks.getCategoryArticles(newArticles, { 'x-locale': locale }, accessToken); + try { + const newArticles = { ...articles, ...data }; + const newData = await sdk.blocks.getCategoryArticles(newArticles, { 'x-locale': locale }, accessToken); - setArticles(newArticles); - setData(newData); + setArticles(newArticles); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx b/packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx index 0f5c9e926..7a1ebe6c4 100644 --- a/packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx +++ b/packages/blocks/invoice-list/src/frontend/InvoiceList.client.tsx @@ -48,20 +48,36 @@ export const InvoiceListPure: React.FC = ({ locale, access const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getInvoiceList(newFilters, { 'x-locale': locale }, accessToken); - - setFilters(newFilters); - setData(newData); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getInvoiceList(newFilters, { 'x-locale': locale }, accessToken); + + setFilters(newFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getInvoiceList(initialFilters, { 'x-locale': locale }, accessToken); - - setFilters(initialFilters); - setData(newData); + try { + const newData = await sdk.blocks.getInvoiceList(initialFilters, { 'x-locale': locale }, accessToken); + + setFilters(initialFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/notification-details/src/frontend/NotificationDetails.client.tsx b/packages/blocks/notification-details/src/frontend/NotificationDetails.client.tsx index 3d2cae2d0..5b6574adc 100644 --- a/packages/blocks/notification-details/src/frontend/NotificationDetails.client.tsx +++ b/packages/blocks/notification-details/src/frontend/NotificationDetails.client.tsx @@ -4,6 +4,10 @@ import React, { useEffect } from 'react'; import { Mappings } from '@o2s/utils.frontend'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { Container } from '@o2s/ui/components/Container'; import { RichText } from '@o2s/ui/components/RichText'; @@ -17,23 +21,39 @@ import { NotificationDetailsPureProps } from './NotificationDetails.types'; export const NotificationDetailsPure: React.FC = ({ locale, ...component }) => { const { data: notification } = component; + const { labels } = useGlobalContext(); useEffect(() => { const markAsViewed = async () => { - await sdk.blocks.markNotificationAs( - { - id: component.id, - status: 'VIEWED', - }, - { 'x-locale': locale }, - component.accessToken, - ); + try { + await sdk.blocks.markNotificationAs( + { + id: component.id, + status: 'VIEWED', + }, + { 'x-locale': locale }, + component.accessToken, + ); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }; if (notification.status.value === 'UNVIEWED') { markAsViewed(); } - }, [component.accessToken, component.id, locale, notification.status.value]); + }, [ + component.accessToken, + component.id, + labels.errors.requestError.content, + labels.errors.requestError.title, + locale, + notification.status.value, + ]); return (
diff --git a/packages/blocks/notification-list/src/frontend/NotificationList.client.tsx b/packages/blocks/notification-list/src/frontend/NotificationList.client.tsx index 0ca404798..b1f4ef202 100644 --- a/packages/blocks/notification-list/src/frontend/NotificationList.client.tsx +++ b/packages/blocks/notification-list/src/frontend/NotificationList.client.tsx @@ -8,6 +8,10 @@ import { Mappings } from '@o2s/utils.frontend'; import { cn } from '@o2s/ui/lib/utils'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import type { DataListColumnConfig } from '@o2s/ui/components/DataList'; import { DataView } from '@o2s/ui/components/DataView'; import { FiltersSection } from '@o2s/ui/components/Filters'; @@ -31,6 +35,7 @@ export const NotificationListPure: React.FC = ({ ...component }) => { const { Link: LinkComponent } = createNavigation(routing); + const { labels } = useGlobalContext(); const initialFilters: Request.GetNotificationListBlockQuery = { id: component.id, @@ -51,20 +56,40 @@ export const NotificationListPure: React.FC = ({ const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getNotificationList(newFilters, { 'x-locale': locale }, accessToken); - - setFilters(newFilters); - setData(newData); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getNotificationList(newFilters, { 'x-locale': locale }, accessToken); + + setFilters(newFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getNotificationList(initialFilters, { 'x-locale': locale }, accessToken); - - setFilters(initialFilters); - setData(newData); + try { + const newData = await sdk.blocks.getNotificationList( + initialFilters, + { 'x-locale': locale }, + accessToken, + ); + + setFilters(initialFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/order-details/src/frontend/OrderDetails.client.tsx b/packages/blocks/order-details/src/frontend/OrderDetails.client.tsx index a318c7707..90845fd5e 100644 --- a/packages/blocks/order-details/src/frontend/OrderDetails.client.tsx +++ b/packages/blocks/order-details/src/frontend/OrderDetails.client.tsx @@ -11,6 +11,10 @@ import { Orders } from '@o2s/framework/modules'; import { cn } from '@o2s/ui/lib/utils'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { ActionList } from '@o2s/ui/components/ActionList'; import { InfoCard } from '@o2s/ui/components/Cards/InfoCard'; import { DynamicIcon } from '@o2s/ui/components/DynamicIcon'; @@ -99,6 +103,7 @@ export const OrderDetailsPure: React.FC> = ({ ...component }) => { const { Link: LinkComponent } = createNavigation(routing); + const { labels } = useGlobalContext(); const initialFilters: Request.GetOrderDetailsBlockQuery = { id: component.id, @@ -114,34 +119,50 @@ export const OrderDetailsPure: React.FC> = ({ const [isPending, startTransition] = useTransition(); - const handleFilter = (data: Partial) => { + const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getOrderDetails( - { - id: orderId, - }, - newFilters, - { 'x-locale': locale }, - accessToken, - ); - setFilters(newFilters); - setItems(newData.productList.products.data); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getOrderDetails( + { + id: orderId, + }, + newFilters, + { 'x-locale': locale }, + accessToken, + ); + setFilters(newFilters); + setItems(newData.productList.products.data); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getOrderDetails( - { - id: orderId, - }, - initialFilters, - { 'x-locale': locale }, - accessToken, - ); - setFilters(initialFilters); - setItems(newData.productList.products.data); + try { + const newData = await sdk.blocks.getOrderDetails( + { + id: orderId, + }, + initialFilters, + { 'x-locale': locale }, + accessToken, + ); + setFilters(initialFilters); + setItems(newData.productList.products.data); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/order-list/src/frontend/OrderList.client.tsx b/packages/blocks/order-list/src/frontend/OrderList.client.tsx index 243b76013..c17b30694 100644 --- a/packages/blocks/order-list/src/frontend/OrderList.client.tsx +++ b/packages/blocks/order-list/src/frontend/OrderList.client.tsx @@ -6,6 +6,10 @@ import React, { useState, useTransition } from 'react'; import { Mappings } from '@o2s/utils.frontend'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import type { DataListColumnConfig } from '@o2s/ui/components/DataList'; import { DataView } from '@o2s/ui/components/DataView'; import { FiltersSection } from '@o2s/ui/components/Filters'; @@ -30,6 +34,7 @@ import { OrderListPureProps } from './OrderList.types'; export const OrderListPure: React.FC = ({ locale, accessToken, routing, ...component }) => { const { Link: LinkComponent } = createNavigation(routing); + const { labels } = useGlobalContext(); const initialFilters: Request.GetOrderListBlockQuery = { id: component.id, @@ -52,18 +57,34 @@ export const OrderListPure: React.FC = ({ locale, accessToke const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getOrderList(newFilters, { 'x-locale': locale }, accessToken); - setFilters(newFilters); - setData(newData); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getOrderList(newFilters, { 'x-locale': locale }, accessToken); + setFilters(newFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getOrderList(initialFilters, { 'x-locale': locale }, accessToken); - setFilters(initialFilters); - setData(newData); + try { + const newData = await sdk.blocks.getOrderList(initialFilters, { 'x-locale': locale }, accessToken); + setFilters(initialFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/orders-summary/src/frontend/OrdersSummary.client.tsx b/packages/blocks/orders-summary/src/frontend/OrdersSummary.client.tsx index d5529f052..8a6e9676a 100644 --- a/packages/blocks/orders-summary/src/frontend/OrdersSummary.client.tsx +++ b/packages/blocks/orders-summary/src/frontend/OrdersSummary.client.tsx @@ -8,6 +8,10 @@ import React, { useState, useTransition } from 'react'; import { cn } from '@o2s/ui/lib/utils'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { InfoCard } from '@o2s/ui/components/Cards/InfoCard'; import { Price } from '@o2s/ui/components/Price'; @@ -54,6 +58,7 @@ export const OrdersSummaryPure: React.FC> = ({ accessToken, ...component }) => { + const { labels } = useGlobalContext(); const initialFilters: Request.GetOrdersSummaryBlockQuery = { id: component.id, dateFrom: dayjs().subtract(6, 'months').toISOString(), @@ -71,32 +76,40 @@ export const OrdersSummaryPure: React.FC> = ({ const handleFilter = (value: string) => { startTransition(async () => { - let dateFrom: string; - const dateTo = dayjs().toISOString(); - - const parts = value.split('-'); - const range = Number(parts[0]!); - const type = parts[1]! as Request.GetOrdersSummaryBlockQuery['range']; - - switch (type) { - case 'day': - dateFrom = dayjs().subtract(range, 'days').toISOString(); - break; - case 'week': - dateFrom = dayjs().subtract(range, 'days').toISOString(); - break; - default: - dateFrom = dayjs().subtract(range, 'months').toISOString(); - break; - } - - const newFilters = { ...filters, dateFrom, dateTo, range: type }; - const newData = await sdk.blocks.getOrdersSummary(newFilters, { 'x-locale': locale }, accessToken); - setFilters(newFilters); - setData(newData); - - if (component.ranges) { - setRange(component.ranges.find((item) => item.value === range && item.type === type)!); + try { + let dateFrom: string; + const dateTo = dayjs().toISOString(); + + const parts = value.split('-'); + const range = Number(parts[0]!); + const type = parts[1]! as Request.GetOrdersSummaryBlockQuery['range']; + + switch (type) { + case 'day': + dateFrom = dayjs().subtract(range, 'days').toISOString(); + break; + case 'week': + dateFrom = dayjs().subtract(range, 'days').toISOString(); + break; + default: + dateFrom = dayjs().subtract(range, 'months').toISOString(); + break; + } + + const newFilters = { ...filters, dateFrom, dateTo, range: type }; + const newData = await sdk.blocks.getOrdersSummary(newFilters, { 'x-locale': locale }, accessToken); + setFilters(newFilters); + setData(newData); + + if (component.ranges) { + setRange(component.ranges.find((item) => item.value === range && item.type === type)!); + } + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); } }); }; diff --git a/packages/blocks/service-list/src/frontend/ServiceList.client.tsx b/packages/blocks/service-list/src/frontend/ServiceList.client.tsx index 7c98789d8..5371a14eb 100644 --- a/packages/blocks/service-list/src/frontend/ServiceList.client.tsx +++ b/packages/blocks/service-list/src/frontend/ServiceList.client.tsx @@ -5,6 +5,10 @@ import React, { useState, useTransition } from 'react'; import { Mappings } from '@o2s/utils.frontend'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { ProductCard, ProductCardBadge } from '@o2s/ui/components/Cards/ProductCard'; import { FiltersSection } from '@o2s/ui/components/Filters'; import { NoResults } from '@o2s/ui/components/NoResults'; @@ -28,21 +32,38 @@ export const ServiceListPure: React.FC = ({ locale, access const [data, setData] = useState(component); const [filters, setFilters] = useState(initialFilters); const [isPending, startTransition] = useTransition(); + const { labels } = useGlobalContext(); const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getServiceList(newFilters, { 'x-locale': locale }, accessToken); - setFilters(newFilters); - setData(newData); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getServiceList(newFilters, { 'x-locale': locale }, accessToken); + setFilters(newFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getServiceList(initialFilters, { 'x-locale': locale }, accessToken); - setFilters(initialFilters); - setData(newData); + try { + const newData = await sdk.blocks.getServiceList(initialFilters, { 'x-locale': locale }, accessToken); + setFilters(initialFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; diff --git a/packages/blocks/ticket-list/src/frontend/TicketList.client.tsx b/packages/blocks/ticket-list/src/frontend/TicketList.client.tsx index a19d85d1a..18a9582b3 100644 --- a/packages/blocks/ticket-list/src/frontend/TicketList.client.tsx +++ b/packages/blocks/ticket-list/src/frontend/TicketList.client.tsx @@ -7,6 +7,10 @@ import React, { useState, useTransition } from 'react'; import { Mappings } from '@o2s/utils.frontend'; +import { toast } from '@o2s/ui/hooks/use-toast'; + +import { useGlobalContext } from '@o2s/ui/providers/GlobalProvider'; + import { ActionList } from '@o2s/ui/components/ActionList'; import type { DataListColumnConfig } from '@o2s/ui/components/DataList'; import { DataView } from '@o2s/ui/components/DataView'; @@ -28,6 +32,7 @@ import { TicketListPureProps } from './TicketList.types'; export const TicketListPure: React.FC = ({ locale, accessToken, routing, meta, ...component }) => { const { Link: LinkComponent } = createNavigation(routing); const inspector = LivePreview.useInspector(); + const { labels } = useGlobalContext(); const initialFilters: Request.GetTicketListBlockQuery = { id: component.id, @@ -49,18 +54,34 @@ export const TicketListPure: React.FC = ({ locale, accessTo const handleFilter = (data: Partial) => { startTransition(async () => { - const newFilters = { ...filters, ...data }; - const newData = await sdk.blocks.getTicketList(newFilters, { 'x-locale': locale }, accessToken); - setFilters(newFilters); - setData(newData); + try { + const newFilters = { ...filters, ...data }; + const newData = await sdk.blocks.getTicketList(newFilters, { 'x-locale': locale }, accessToken); + setFilters(newFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); }; const handleReset = () => { startTransition(async () => { - const newData = await sdk.blocks.getTicketList(initialFilters, { 'x-locale': locale }, accessToken); - setFilters(initialFilters); - setData(newData); + try { + const newData = await sdk.blocks.getTicketList(initialFilters, { 'x-locale': locale }, accessToken); + setFilters(initialFilters); + setData(newData); + } catch (_error) { + toast({ + variant: 'destructive', + title: labels.errors.requestError.title, + description: labels.errors.requestError.content, + }); + } }); };