@deenruv/admin-dashboard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +30 -0
- package/dist/@types/resources.js +51 -0
- package/dist/DeenruvAdminPanel.js +118 -0
- package/dist/DeenruvDeveloperIndicator.js +57 -0
- package/dist/components/Aexol.js +4 -0
- package/dist/components/BrandLogo.js +22 -0
- package/dist/components/CanLeaveRouteDialog.js +8 -0
- package/dist/components/DataTable.js +13 -0
- package/dist/components/DeleteDialog.js +6 -0
- package/dist/components/DuplicateEntry.js +46 -0
- package/dist/components/GlobalSearch.js +134 -0
- package/dist/components/History/AddEntryForm.js +29 -0
- package/dist/components/History/DeleteEntryDialog.js +25 -0
- package/dist/components/History/EditEntryDialog.js +32 -0
- package/dist/components/History/History.js +13 -0
- package/dist/components/History/ModifyHistoryInfo.js +6 -0
- package/dist/components/History/Timeline.js +53 -0
- package/dist/components/History/index.js +5 -0
- package/dist/components/Menu/ActiveAdmins.js +24 -0
- package/dist/components/Menu/ChannelSwitcher.js +20 -0
- package/dist/components/Menu/LanguagesDropdown.js +26 -0
- package/dist/components/Menu/Navigation.js +270 -0
- package/dist/components/Menu/NavigationFooter.js +12 -0
- package/dist/components/Menu/Notifications.js +90 -0
- package/dist/components/Menu/index.js +67 -0
- package/dist/components/index.js +6 -0
- package/dist/graphql/base.js +95 -0
- package/dist/graphql/collections.js +94 -0
- package/dist/graphql/draft_order.js +307 -0
- package/dist/graphql/orders.js +157 -0
- package/dist/graphql/products.js +230 -0
- package/dist/graphql/scalars.js +22 -0
- package/dist/i18.js +13 -0
- package/dist/index.css +161 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1 -0
- package/dist/locales/en/admins.json +57 -0
- package/dist/locales/en/assets.json +69 -0
- package/dist/locales/en/channels.json +66 -0
- package/dist/locales/en/collections.json +145 -0
- package/dist/locales/en/common.json +1127 -0
- package/dist/locales/en/countries.json +50 -0
- package/dist/locales/en/customerGroups.json +6 -0
- package/dist/locales/en/customers.json +109 -0
- package/dist/locales/en/dashboard.json +34 -0
- package/dist/locales/en/facets.json +78 -0
- package/dist/locales/en/globalSettings.json +15 -0
- package/dist/locales/en/index.js +52 -0
- package/dist/locales/en/orders.json +932 -0
- package/dist/locales/en/paymentMethods.json +59 -0
- package/dist/locales/en/permissions.json +94 -0
- package/dist/locales/en/products.json +194 -0
- package/dist/locales/en/promotions.json +65 -0
- package/dist/locales/en/roles.json +59 -0
- package/dist/locales/en/sellers.json +41 -0
- package/dist/locales/en/shippingMethods.json +84 -0
- package/dist/locales/en/stockLocations.json +40 -0
- package/dist/locales/en/system.json +41 -0
- package/dist/locales/en/table.json +201 -0
- package/dist/locales/en/taxCategories.json +47 -0
- package/dist/locales/en/taxRates.json +56 -0
- package/dist/locales/en/zones.json +47 -0
- package/dist/locales/index.js +2 -0
- package/dist/locales/pl/admins.json +57 -0
- package/dist/locales/pl/assets.json +69 -0
- package/dist/locales/pl/channels.json +66 -0
- package/dist/locales/pl/collections.json +145 -0
- package/dist/locales/pl/common.json +1128 -0
- package/dist/locales/pl/countries.json +50 -0
- package/dist/locales/pl/customerGroups.json +6 -0
- package/dist/locales/pl/customers.json +109 -0
- package/dist/locales/pl/dashboard.json +34 -0
- package/dist/locales/pl/facets.json +78 -0
- package/dist/locales/pl/globalSettings.json +15 -0
- package/dist/locales/pl/index.js +52 -0
- package/dist/locales/pl/orders.json +932 -0
- package/dist/locales/pl/paymentMethods.json +59 -0
- package/dist/locales/pl/permissions.json +94 -0
- package/dist/locales/pl/products.json +194 -0
- package/dist/locales/pl/promotions.json +65 -0
- package/dist/locales/pl/roles.json +59 -0
- package/dist/locales/pl/sellers.json +41 -0
- package/dist/locales/pl/shippingMethods.json +84 -0
- package/dist/locales/pl/stockLocations.json +40 -0
- package/dist/locales/pl/system.json +41 -0
- package/dist/locales/pl/table.json +201 -0
- package/dist/locales/pl/taxCategories.json +47 -0
- package/dist/locales/pl/taxRates.json +56 -0
- package/dist/locales/pl/zones.json +47 -0
- package/dist/notifications/OrderStatusNotification.js +47 -0
- package/dist/notifications/SystemStatusNotification.js +19 -0
- package/dist/pages/Custom404.js +5 -0
- package/dist/pages/LoginScreen.js +37 -0
- package/dist/pages/Root.js +252 -0
- package/dist/pages/admins/Detail.js +72 -0
- package/dist/pages/admins/List.js +56 -0
- package/dist/pages/admins/_components/AdminDetailView.js +34 -0
- package/dist/pages/admins/_components/RolesCard.js +31 -0
- package/dist/pages/admins/index.js +2 -0
- package/dist/pages/assets/List.js +82 -0
- package/dist/pages/assets/_components/Asset.js +114 -0
- package/dist/pages/assets/_components/AssetListView.js +29 -0
- package/dist/pages/assets/_components/EditAssetDialog.js +85 -0
- package/dist/pages/assets/_components/UploadAssetDialog.js +133 -0
- package/dist/pages/assets/_components/UploadProgress.js +20 -0
- package/dist/pages/assets/index.js +1 -0
- package/dist/pages/channels/Detail.js +149 -0
- package/dist/pages/channels/List.js +46 -0
- package/dist/pages/channels/_components/ChannelDetailView.js +51 -0
- package/dist/pages/channels/_components/DefaultsCard.js +25 -0
- package/dist/pages/channels/index.js +2 -0
- package/dist/pages/collections/Detail.js +61 -0
- package/dist/pages/collections/List.js +135 -0
- package/dist/pages/collections/_components/CollectionDetailView.js +52 -0
- package/dist/pages/collections/_components/CollectionProductVariantsDrawer.js +28 -0
- package/dist/pages/collections/_components/CombinationMode.js +6 -0
- package/dist/pages/collections/_components/ContentsCard.js +43 -0
- package/dist/pages/collections/_components/ContentsTable.js +102 -0
- package/dist/pages/collections/_components/DeleteCollectionsFromChannel.js +36 -0
- package/dist/pages/collections/_components/FacetsSelector.js +30 -0
- package/dist/pages/collections/_components/FiltersCard.js +101 -0
- package/dist/pages/collections/_components/MoveCollectionsToCollections.js +107 -0
- package/dist/pages/collections/_components/MoveEntityToChannels.js +115 -0
- package/dist/pages/collections/_components/PageHeader.js +11 -0
- package/dist/pages/collections/_components/SelectedCollectionsModal.js +22 -0
- package/dist/pages/collections/_components/VariantsSelector.js +43 -0
- package/dist/pages/collections/consts.js +7 -0
- package/dist/pages/collections/index.js +2 -0
- package/dist/pages/countries/Detail.js +57 -0
- package/dist/pages/countries/List.js +47 -0
- package/dist/pages/countries/_components/CountryDetailView.js +28 -0
- package/dist/pages/countries/index.js +2 -0
- package/dist/pages/customer-groups/Detail.js +76 -0
- package/dist/pages/customer-groups/List.js +41 -0
- package/dist/pages/customer-groups/_components/CustomerGroupsDetailView.js +21 -0
- package/dist/pages/customer-groups/index.js +2 -0
- package/dist/pages/customers/Detail.js +75 -0
- package/dist/pages/customers/List.js +60 -0
- package/dist/pages/customers/_components/Address.js +59 -0
- package/dist/pages/customers/_components/AddressDialog.js +56 -0
- package/dist/pages/customers/_components/AddressForm.js +77 -0
- package/dist/pages/customers/_components/AddressesCard.js +9 -0
- package/dist/pages/customers/_components/CustomerDetailSidebar.js +9 -0
- package/dist/pages/customers/_components/CustomerDetailView.js +41 -0
- package/dist/pages/customers/_components/CustomerGroupsCard.js +46 -0
- package/dist/pages/customers/_components/HistoryTab.js +65 -0
- package/dist/pages/customers/_components/OrdersTab.js +46 -0
- package/dist/pages/customers/_components/PersonalDataCard.js +6 -0
- package/dist/pages/customers/_components/VerifiedCard.js +6 -0
- package/dist/pages/customers/index.js +2 -0
- package/dist/pages/dashboard/Dashboard.js +10 -0
- package/dist/pages/dashboard/index.js +1 -0
- package/dist/pages/extensions/Extensions.js +74 -0
- package/dist/pages/extensions/index.js +1 -0
- package/dist/pages/facets/Detail.js +56 -0
- package/dist/pages/facets/List.js +48 -0
- package/dist/pages/facets/_components/AddFacetValueDialog.js +105 -0
- package/dist/pages/facets/_components/ColorSample.js +18 -0
- package/dist/pages/facets/_components/FacetDetailView.js +98 -0
- package/dist/pages/facets/index.js +2 -0
- package/dist/pages/global-settings/GlobalSettingsComponent.js +30 -0
- package/dist/pages/global-settings/index.js +71 -0
- package/dist/pages/index.js +25 -0
- package/dist/pages/orders/Detail.js +21 -0
- package/dist/pages/orders/List.js +132 -0
- package/dist/pages/orders/_components/AddPaymentDialog.js +50 -0
- package/dist/pages/orders/_components/AddressCard.js +260 -0
- package/dist/pages/orders/_components/CancelAndRefundDialog.js +29 -0
- package/dist/pages/orders/_components/ChangesRegister.js +68 -0
- package/dist/pages/orders/_components/ChangesRegisterTable.js +18 -0
- package/dist/pages/orders/_components/CouponCodesCard.js +85 -0
- package/dist/pages/orders/_components/CustomComponent.js +21 -0
- package/dist/pages/orders/_components/CustomerSelectCard.js +117 -0
- package/dist/pages/orders/_components/EditNoteButton.js +10 -0
- package/dist/pages/orders/_components/FulfillmentModal.js +85 -0
- package/dist/pages/orders/_components/LineItem.js +7 -0
- package/dist/pages/orders/_components/ManualOrderChangeModal.js +47 -0
- package/dist/pages/orders/_components/ModifyAcceptModal.js +22 -0
- package/dist/pages/orders/_components/ModifyingCard.js +77 -0
- package/dist/pages/orders/_components/OrderHistory.js +59 -0
- package/dist/pages/orders/_components/OrderLineActionModal/ActionQuantityPrice.js +58 -0
- package/dist/pages/orders/_components/OrderLineActionModal/index.js +7 -0
- package/dist/pages/orders/_components/OrderLineActionModal/types.js +1 -0
- package/dist/pages/orders/_components/OrderLineCustomFields.js +26 -0
- package/dist/pages/orders/_components/OrderSummary.js +10 -0
- package/dist/pages/orders/_components/PaymentMetadata.js +6 -0
- package/dist/pages/orders/_components/Payments.js +113 -0
- package/dist/pages/orders/_components/PossibleOrderStates.js +54 -0
- package/dist/pages/orders/_components/PriceChangedInfo.js +16 -0
- package/dist/pages/orders/_components/ProductsCard.js +308 -0
- package/dist/pages/orders/_components/ProductsTable.js +29 -0
- package/dist/pages/orders/_components/PromotionsList.js +73 -0
- package/dist/pages/orders/_components/RealizationCard.js +98 -0
- package/dist/pages/orders/_components/RefundCard.js +11 -0
- package/dist/pages/orders/_components/RefundPaymentCard.js +13 -0
- package/dist/pages/orders/_components/ShippingMethod.js +112 -0
- package/dist/pages/orders/_components/SpecialLineItem.js +7 -0
- package/dist/pages/orders/_components/SurchargeCard.js +99 -0
- package/dist/pages/orders/_components/SurchargeTable.js +8 -0
- package/dist/pages/orders/_components/TaxSummary.js +11 -0
- package/dist/pages/orders/_components/ToRealizationForm.js +64 -0
- package/dist/pages/orders/_components/TopActions.js +219 -0
- package/dist/pages/orders/_components/index.js +25 -0
- package/dist/pages/orders/index.js +2 -0
- package/dist/pages/payment-methods/Detail.js +67 -0
- package/dist/pages/payment-methods/List.js +41 -0
- package/dist/pages/payment-methods/_components/OptionsCard.js +60 -0
- package/dist/pages/payment-methods/_components/PaymentMethodDetailView.js +53 -0
- package/dist/pages/payment-methods/index.js +2 -0
- package/dist/pages/product-variants/Detail.js +4 -0
- package/dist/pages/product-variants/List.js +76 -0
- package/dist/pages/product-variants/index.js +2 -0
- package/dist/pages/products/Detail.js +75 -0
- package/dist/pages/products/List.js +79 -0
- package/dist/pages/products/_components/AddOptionGroupDialog.js +65 -0
- package/dist/pages/products/_components/AssetsCard.js +35 -0
- package/dist/pages/products/_components/BasicFieldsCard.js +6 -0
- package/dist/pages/products/_components/ChannelsCard.js +6 -0
- package/dist/pages/products/_components/CollectionsCard.js +6 -0
- package/dist/pages/products/_components/Container.js +4 -0
- package/dist/pages/products/_components/DiscountRatingCard.js +6 -0
- package/dist/pages/products/_components/FacetValuesCard.js +6 -0
- package/dist/pages/products/_components/ImagesCard.js +6 -0
- package/dist/pages/products/_components/OptionGroup.js +93 -0
- package/dist/pages/products/_components/OptionValueCard.js +59 -0
- package/dist/pages/products/_components/OptionsCard.js +38 -0
- package/dist/pages/products/_components/OptionsTab.js +40 -0
- package/dist/pages/products/_components/PriceCard.js +27 -0
- package/dist/pages/products/_components/ProductDetailSidebar.js +30 -0
- package/dist/pages/products/_components/ProductDetailView.js +54 -0
- package/dist/pages/products/_components/SettingsCard.js +6 -0
- package/dist/pages/products/_components/StockCard.js +40 -0
- package/dist/pages/products/_components/Variant.js +149 -0
- package/dist/pages/products/_components/VariantsTab.js +46 -0
- package/dist/pages/products/index.js +2 -0
- package/dist/pages/promotions/Detail.js +80 -0
- package/dist/pages/promotions/List.js +38 -0
- package/dist/pages/promotions/_components/ActionsCard.js +67 -0
- package/dist/pages/promotions/_components/BasicFieldsCard.js +6 -0
- package/dist/pages/promotions/_components/ConditionsCard.js +70 -0
- package/dist/pages/promotions/_components/CustomerGroupsSelector.js +25 -0
- package/dist/pages/promotions/_components/EnabledCard.js +6 -0
- package/dist/pages/promotions/_components/OptionsCard.js +6 -0
- package/dist/pages/promotions/_components/PromotionDetailSidebar.js +15 -0
- package/dist/pages/promotions/_components/PromotionDetailView.js +66 -0
- package/dist/pages/promotions/_components/PromotionFieldRender.js +4 -0
- package/dist/pages/promotions/index.js +2 -0
- package/dist/pages/roles/Detail.js +72 -0
- package/dist/pages/roles/List.js +70 -0
- package/dist/pages/roles/_components/PermissionsCard.js +7 -0
- package/dist/pages/roles/_components/PermissionsTable.js +42 -0
- package/dist/pages/roles/_components/RoleDetailView.js +49 -0
- package/dist/pages/roles/index.js +2 -0
- package/dist/pages/sellers/Detail.js +48 -0
- package/dist/pages/sellers/List.js +37 -0
- package/dist/pages/sellers/_components/SellerDetailView.js +20 -0
- package/dist/pages/sellers/index.js +2 -0
- package/dist/pages/shipping-methods/Detail.js +105 -0
- package/dist/pages/shipping-methods/List.js +40 -0
- package/dist/pages/shipping-methods/_components/CalculatorCard.js +79 -0
- package/dist/pages/shipping-methods/_components/CheckerCard.js +76 -0
- package/dist/pages/shipping-methods/_components/Lines.js +20 -0
- package/dist/pages/shipping-methods/_components/ShippingMethodDetailView.js +67 -0
- package/dist/pages/shipping-methods/_components/TestCard.js +62 -0
- package/dist/pages/shipping-methods/index.js +2 -0
- package/dist/pages/status/Status.js +6 -0
- package/dist/pages/status/_components/FilterToolbar.js +29 -0
- package/dist/pages/status/_components/Health.js +21 -0
- package/dist/pages/status/_components/JobResultPopover.js +8 -0
- package/dist/pages/status/_components/Jobs.js +199 -0
- package/dist/pages/status/_components/JsonExplorer.js +44 -0
- package/dist/pages/status/_components/JsonPopup.js +8 -0
- package/dist/pages/status/index.js +1 -0
- package/dist/pages/stock-locations/Detail.js +77 -0
- package/dist/pages/stock-locations/List.js +38 -0
- package/dist/pages/stock-locations/_components/StockLocationDetailView.js +22 -0
- package/dist/pages/stock-locations/index.js +2 -0
- package/dist/pages/tax-categories/Detail.js +85 -0
- package/dist/pages/tax-categories/List.js +46 -0
- package/dist/pages/tax-categories/_components/TaxCategoryDetailView.js +24 -0
- package/dist/pages/tax-categories/index.js +2 -0
- package/dist/pages/tax-rates/Detail.js +62 -0
- package/dist/pages/tax-rates/List.js +64 -0
- package/dist/pages/tax-rates/_components/TaxRateDetailView.js +64 -0
- package/dist/pages/tax-rates/index.js +2 -0
- package/dist/pages/zones/Detail.js +86 -0
- package/dist/pages/zones/List.js +52 -0
- package/dist/pages/zones/_components/ZoneDetailView.js +49 -0
- package/dist/pages/zones/index.js +2 -0
- package/dist/version.js +1 -0
- package/package.json +122 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CanLeaveRouteDialog } from "../components";
|
|
3
|
+
import { serverConfigSelector, configurableOperationDefinitionSelector, countrySelector, apiClient, useOrder, useNotifications, DEFAULT_CHANNEL_CODE, useTranslation, capitalizeFirstLetter, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { useCallback, useEffect } from 'react';
|
|
5
|
+
import { Outlet, useLocation, useSearchParams } from 'react-router-dom';
|
|
6
|
+
import { toast } from 'sonner';
|
|
7
|
+
import { activeAdministratorSelector, paymentMethodsSelector, useServer, useSettings, } from '@deenruv/react-ui-devkit';
|
|
8
|
+
import { Menu } from "../components";
|
|
9
|
+
import { GlobalSearch } from "../components/GlobalSearch.js";
|
|
10
|
+
import { DeenruvDeveloperIndicator } from "../DeenruvDeveloperIndicator.js";
|
|
11
|
+
import { Permission } from '@deenruv/admin-types';
|
|
12
|
+
const TAKE = 100;
|
|
13
|
+
const getAllPaginatedCountries = async () => {
|
|
14
|
+
let countries = [];
|
|
15
|
+
let totalItems = 0;
|
|
16
|
+
let skip = 0;
|
|
17
|
+
do {
|
|
18
|
+
const { countries: { items, totalItems: total }, } = await apiClient('query')({
|
|
19
|
+
countries: [{ options: { skip, take: TAKE } }, { items: countrySelector, totalItems: true }],
|
|
20
|
+
});
|
|
21
|
+
countries = [...countries, ...items];
|
|
22
|
+
totalItems = total;
|
|
23
|
+
skip += TAKE;
|
|
24
|
+
} while (countries.length < totalItems);
|
|
25
|
+
return { countries };
|
|
26
|
+
};
|
|
27
|
+
const getAllPaymentMethods = async () => {
|
|
28
|
+
let paymentMethods = [];
|
|
29
|
+
let totalItems = 0;
|
|
30
|
+
let skip = 0;
|
|
31
|
+
do {
|
|
32
|
+
const { paymentMethods: { items, totalItems: total }, } = await apiClient('query')({
|
|
33
|
+
paymentMethods: [
|
|
34
|
+
{ options: { skip, take: TAKE, filter: { enabled: { eq: true } } } },
|
|
35
|
+
{ items: paymentMethodsSelector, totalItems: true },
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
paymentMethods = [...paymentMethods, ...items];
|
|
39
|
+
totalItems = total;
|
|
40
|
+
skip += TAKE;
|
|
41
|
+
} while (paymentMethods.length < totalItems);
|
|
42
|
+
return { paymentMethods };
|
|
43
|
+
};
|
|
44
|
+
export const Root = ({ allPaths }) => {
|
|
45
|
+
const isLocalhost = window.location.hostname === 'localhost';
|
|
46
|
+
const { t } = useTranslation('common');
|
|
47
|
+
const setActiveAdministrator = useServer((p) => p.setActiveAdministrator);
|
|
48
|
+
const setUserPermissions = useServer((p) => p.setUserPermissions);
|
|
49
|
+
const setServerConfig = useServer((p) => p.setServerConfig);
|
|
50
|
+
const setCountries = useServer((p) => p.setCountries);
|
|
51
|
+
const setFulfillmentHandlers = useServer((p) => p.setFulfillmentHandlers);
|
|
52
|
+
const setPaymentMethodsType = useServer((p) => p.setPaymentMethodsType);
|
|
53
|
+
const fetchPendingJobs = useServer((p) => p.fetchPendingJobs);
|
|
54
|
+
const loaded = useServer((p) => p.loaded);
|
|
55
|
+
const setLoaded = useServer((p) => p.setLoaded);
|
|
56
|
+
const setAvailableLanguages = useSettings((p) => p.setAvailableLanguages);
|
|
57
|
+
const setLanguage = useSettings((p) => p.setLanguage);
|
|
58
|
+
const setTranslationLanguage = useSettings((p) => p.setTranslationsLanguage);
|
|
59
|
+
const selectedChannel = useSettings((p) => p.selectedChannel);
|
|
60
|
+
const setSelectedChannel = useSettings((p) => p.setSelectedChannel);
|
|
61
|
+
const setChannels = useServer((p) => p.setChannels);
|
|
62
|
+
const fetchGraphQLSchema = useServer((p) => p.fetchGraphQLSchema);
|
|
63
|
+
const fetchStatus = useServer((p) => p.fetchStatus);
|
|
64
|
+
const { initializeOrderCustomFields } = useOrder();
|
|
65
|
+
const setData = useNotifications(({ setData }) => setData);
|
|
66
|
+
const notifications = useNotifications(({ notifications }) => notifications);
|
|
67
|
+
const channel = useSettings((p) => p.selectedChannel);
|
|
68
|
+
const location = useLocation();
|
|
69
|
+
const [searchParams] = useSearchParams();
|
|
70
|
+
const managePageTitle = useCallback(() => {
|
|
71
|
+
const pathname = location.pathname;
|
|
72
|
+
const isDeenruvPath = allPaths.some((path) => pathname.startsWith(path));
|
|
73
|
+
if (isDeenruvPath) {
|
|
74
|
+
let [, , route, id] = pathname.split('/');
|
|
75
|
+
if (!route) {
|
|
76
|
+
document.title = [
|
|
77
|
+
window.__DEENRUV_SETTINGS__.branding.name,
|
|
78
|
+
capitalizeFirstLetter(t(`menu.dashboard`)),
|
|
79
|
+
'Admin UI',
|
|
80
|
+
].join(' - ');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (route?.split('-').length > 1) {
|
|
84
|
+
const [first, second] = route.split('-');
|
|
85
|
+
route = `${first}${second.charAt(0).toUpperCase()}${second.slice(1)}`;
|
|
86
|
+
}
|
|
87
|
+
if (isNaN(parseInt(id, 10))) {
|
|
88
|
+
document.title = [
|
|
89
|
+
window.__DEENRUV_SETTINGS__.branding.name,
|
|
90
|
+
capitalizeFirstLetter(t(`menu.${route}`)),
|
|
91
|
+
'Admin UI',
|
|
92
|
+
].join(' - ');
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const tab = searchParams.get('tab');
|
|
96
|
+
if (tab) {
|
|
97
|
+
const variantID = searchParams.get('variantId');
|
|
98
|
+
if (variantID)
|
|
99
|
+
id = variantID;
|
|
100
|
+
document.title = [
|
|
101
|
+
window.__DEENRUV_SETTINGS__.branding.name,
|
|
102
|
+
capitalizeFirstLetter(t(`menu.${tab}`)),
|
|
103
|
+
id,
|
|
104
|
+
'Admin UI',
|
|
105
|
+
].join(' - ');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
document.title = [
|
|
109
|
+
window.__DEENRUV_SETTINGS__.branding.name,
|
|
110
|
+
capitalizeFirstLetter(t(`menu.${route}`)),
|
|
111
|
+
id,
|
|
112
|
+
'Admin UI',
|
|
113
|
+
].join(' - ');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}, [location, searchParams]);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
managePageTitle();
|
|
121
|
+
}, [location, searchParams]);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const intervalId = setInterval(() => {
|
|
124
|
+
fetchStatus();
|
|
125
|
+
}, 10000);
|
|
126
|
+
return () => clearInterval(intervalId);
|
|
127
|
+
}, [fetchStatus]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!loaded)
|
|
130
|
+
return;
|
|
131
|
+
const intervalIds = notifications.map(({ id, fetch, interval }) => {
|
|
132
|
+
const fetchData = async () => {
|
|
133
|
+
const data = await fetch();
|
|
134
|
+
setData(id, data);
|
|
135
|
+
};
|
|
136
|
+
fetchData();
|
|
137
|
+
const intervalId = setInterval(fetchData, interval);
|
|
138
|
+
return intervalId;
|
|
139
|
+
});
|
|
140
|
+
return () => {
|
|
141
|
+
intervalIds.forEach(clearInterval);
|
|
142
|
+
};
|
|
143
|
+
}, [notifications.map((n) => n.id).join(','), loaded]);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const init = async () => {
|
|
146
|
+
const activeAdministratorResponse = await apiClient('query')({
|
|
147
|
+
activeAdministrator: activeAdministratorSelector,
|
|
148
|
+
});
|
|
149
|
+
const roles = activeAdministratorResponse.activeAdministrator?.user.roles || [];
|
|
150
|
+
if (!activeAdministratorResponse.activeAdministrator) {
|
|
151
|
+
toast.error(t('setup.failedAdmin'));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
setActiveAdministrator(activeAdministratorResponse.activeAdministrator);
|
|
155
|
+
setUserPermissions(Array.from(new Set(roles.flatMap((role) => role.permissions))));
|
|
156
|
+
if ([Permission.ReadChannel].some((p) => roles.some((r) => r.permissions.includes(p)))) {
|
|
157
|
+
const { channels: { items: allChannels = [] }, } = await apiClient('query')({
|
|
158
|
+
channels: [
|
|
159
|
+
{},
|
|
160
|
+
{
|
|
161
|
+
items: {
|
|
162
|
+
id: true,
|
|
163
|
+
code: true,
|
|
164
|
+
token: true,
|
|
165
|
+
currencyCode: true,
|
|
166
|
+
defaultLanguageCode: true,
|
|
167
|
+
availableLanguageCodes: true,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
});
|
|
172
|
+
setChannels(allChannels);
|
|
173
|
+
if (!channel) {
|
|
174
|
+
if (selectedChannel) {
|
|
175
|
+
const foundChannel = allChannels.find((ch) => ch.code === selectedChannel.code);
|
|
176
|
+
setSelectedChannel(foundChannel || allChannels[0]);
|
|
177
|
+
}
|
|
178
|
+
const existingChannel = allChannels.find((ch) => ch.code === window?.__DEENRUV_SETTINGS__?.ui?.defaultChannelCode);
|
|
179
|
+
if (existingChannel) {
|
|
180
|
+
setSelectedChannel(existingChannel);
|
|
181
|
+
}
|
|
182
|
+
const defaultChannel = allChannels.find((ch) => ch.code === DEFAULT_CHANNEL_CODE) || allChannels[0];
|
|
183
|
+
setSelectedChannel(defaultChannel);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const possibleChannels = roles.flatMap((role) => role.channels);
|
|
188
|
+
if (possibleChannels.length > 0) {
|
|
189
|
+
setSelectedChannel(possibleChannels[0]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// WE NEED TO CHECK IF LOCALSTORAGE HAS LANGUAGE SET
|
|
194
|
+
if (window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode) {
|
|
195
|
+
// window?.__DEENRUV_SETTINGS__.i18n.changeLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode);
|
|
196
|
+
// setLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultLanguageCode);
|
|
197
|
+
}
|
|
198
|
+
// WE NEED TO CHECK IF LOCALSTORAGE HAS LANGUAGE SET
|
|
199
|
+
if (window?.__DEENRUV_SETTINGS__?.ui?.defaultTranslationLanguageCode) {
|
|
200
|
+
// setTranslationLanguage(window?.__DEENRUV_SETTINGS__?.ui?.defaultTranslationLanguageCode);
|
|
201
|
+
}
|
|
202
|
+
const { globalSettings } = await apiClient('query')({
|
|
203
|
+
globalSettings: { serverConfig: serverConfigSelector, availableLanguages: true },
|
|
204
|
+
});
|
|
205
|
+
fetchPendingJobs();
|
|
206
|
+
initializeOrderCustomFields(globalSettings.serverConfig);
|
|
207
|
+
setLoaded(true);
|
|
208
|
+
if ([Permission.ReadCountry, Permission.ReadPaymentMethod, Permission.ReadOrder].some((p) => roles.some((r) => r.permissions.includes(p)))) {
|
|
209
|
+
const [countriesResponse, paymentsResponse, fulfillmentsResponse] = await Promise.allSettled([
|
|
210
|
+
getAllPaginatedCountries(),
|
|
211
|
+
getAllPaymentMethods(),
|
|
212
|
+
apiClient('query')({ fulfillmentHandlers: configurableOperationDefinitionSelector }),
|
|
213
|
+
]);
|
|
214
|
+
if (countriesResponse.status === 'rejected') {
|
|
215
|
+
toast.error(t('setup.failedServer'));
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
setServerConfig(globalSettings.serverConfig);
|
|
219
|
+
setAvailableLanguages(globalSettings.availableLanguages);
|
|
220
|
+
// const socket = serverConfigResponse.value.globalSettings.serverConfig.plugins?.find(
|
|
221
|
+
// (plugin) => plugin.name === 'AexolAdminsPlugin',
|
|
222
|
+
// );
|
|
223
|
+
// if (socket && socket.active) setNeedSocket(true);
|
|
224
|
+
}
|
|
225
|
+
if (countriesResponse.status === 'rejected') {
|
|
226
|
+
toast.error(t('setup.failedCountries'));
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
setCountries(countriesResponse.value.countries);
|
|
230
|
+
}
|
|
231
|
+
if (paymentsResponse.status === 'rejected') {
|
|
232
|
+
toast.error(t('setup.failedPayments'));
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
setPaymentMethodsType(paymentsResponse.value.paymentMethods);
|
|
236
|
+
}
|
|
237
|
+
if (fulfillmentsResponse.status === 'rejected') {
|
|
238
|
+
toast.error(t('setup.failedFulfillments'));
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
setFulfillmentHandlers(fulfillmentsResponse.value?.fulfillmentHandlers);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
fetchGraphQLSchema().then(async (schema) => {
|
|
246
|
+
window.__DEENRUV_SCHEMA__ = schema;
|
|
247
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
248
|
+
await init();
|
|
249
|
+
});
|
|
250
|
+
}, []);
|
|
251
|
+
return (_jsx(_Fragment, { children: _jsx("div", { className: "bg-background text-foreground flex max-h-screen w-full max-w-full overflow-hidden", children: loaded ? (_jsxs(_Fragment, { children: [_jsx(Menu, { children: _jsx(Outlet, {}) }), _jsx(GlobalSearch, {}), _jsx(CanLeaveRouteDialog, {}), isLocalhost ? _jsx(DeenruvDeveloperIndicator, {}) : null] })) : (_jsx("div", { className: "flex h-screen w-full items-center justify-center", children: _jsx("span", { className: "animate-spin", children: "\u23F3" }) })) }) }));
|
|
252
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { useParams } from 'react-router-dom';
|
|
4
|
+
import { DetailView, createDeenruvForm, getMutation, useMutation, useValidators, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
5
|
+
import { AdminDetailView } from "./_components/AdminDetailView.js";
|
|
6
|
+
import { Permission } from '@deenruv/admin-types';
|
|
7
|
+
const CreateAdminMutation = getMutation('createAdministrator');
|
|
8
|
+
const EditAdminMutation = getMutation('updateAdministrator');
|
|
9
|
+
const DeleteAdminMutation = getMutation('deleteAdministrator');
|
|
10
|
+
export const AdminsDetailPage = () => {
|
|
11
|
+
const { id } = useParams();
|
|
12
|
+
const [update] = useMutation(EditAdminMutation);
|
|
13
|
+
const [create] = useMutation(CreateAdminMutation);
|
|
14
|
+
const [remove] = useMutation(DeleteAdminMutation);
|
|
15
|
+
const editMode = useMemo(() => !!id, [id]);
|
|
16
|
+
const { t } = useTranslation('admins');
|
|
17
|
+
const { emailValidator, stringValidator, arrayValidator } = useValidators();
|
|
18
|
+
const onSubmitHandler = useCallback((data) => {
|
|
19
|
+
if (!data.emailAddress?.validatedValue) {
|
|
20
|
+
throw new Error('Name is required.');
|
|
21
|
+
}
|
|
22
|
+
const inputData = {
|
|
23
|
+
emailAddress: data.emailAddress?.validatedValue,
|
|
24
|
+
firstName: data.firstName?.validatedValue,
|
|
25
|
+
lastName: data.lastName?.validatedValue,
|
|
26
|
+
password: data.password?.validatedValue ? data.password?.validatedValue : undefined,
|
|
27
|
+
roleIds: data.roleIds?.validatedValue,
|
|
28
|
+
...(data.customFields?.validatedValue ? { customFields: data.customFields?.validatedValue } : {}),
|
|
29
|
+
};
|
|
30
|
+
if (id) {
|
|
31
|
+
return update({
|
|
32
|
+
input: {
|
|
33
|
+
id,
|
|
34
|
+
...inputData,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return create({
|
|
40
|
+
input: inputData,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}, [id, update, create]);
|
|
44
|
+
const onDeleteHandler = useCallback(() => {
|
|
45
|
+
if (!id) {
|
|
46
|
+
throw new Error('Could not find the id.');
|
|
47
|
+
}
|
|
48
|
+
return remove({ input: { id } });
|
|
49
|
+
}, [remove, id]);
|
|
50
|
+
return (_jsx("div", { className: "relative flex flex-col gap-y-4", children: _jsx(DetailView, { id: id, locationId: "admins-detail-view", main: {
|
|
51
|
+
name: 'admin',
|
|
52
|
+
label: 'Admin',
|
|
53
|
+
component: _jsx(AdminDetailView, {}),
|
|
54
|
+
form: createDeenruvForm({
|
|
55
|
+
key: 'CreateAdministratorInput',
|
|
56
|
+
keys: ['firstName', 'lastName', 'emailAddress', 'password', 'roleIds', 'customFields'],
|
|
57
|
+
config: {
|
|
58
|
+
emailAddress: emailValidator,
|
|
59
|
+
firstName: stringValidator(t('validation.firstNameRequired')),
|
|
60
|
+
lastName: stringValidator(t('validation.lastNameRequired')),
|
|
61
|
+
password: !editMode ? stringValidator(t('validation.passwordRequired')) : undefined,
|
|
62
|
+
roleIds: arrayValidator(t('validation.rolesRequired')),
|
|
63
|
+
},
|
|
64
|
+
onSubmitted: onSubmitHandler,
|
|
65
|
+
onDeleted: onDeleteHandler,
|
|
66
|
+
}),
|
|
67
|
+
}, permissions: {
|
|
68
|
+
create: Permission.CreateAdministrator,
|
|
69
|
+
delete: Permission.DeleteAdministrator,
|
|
70
|
+
edit: Permission.UpdateAdministrator,
|
|
71
|
+
} }) }));
|
|
72
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Routes, apiClient, DetailList, deepMerge, ListBadge, ListLocations, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Permission, SortOrder } from '@deenruv/admin-types';
|
|
4
|
+
const tableId = 'admins-list-view';
|
|
5
|
+
const { selector } = ListLocations[tableId];
|
|
6
|
+
const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
|
|
7
|
+
const response = await apiClient('query')({
|
|
8
|
+
administrators: [
|
|
9
|
+
{
|
|
10
|
+
options: {
|
|
11
|
+
take: perPage,
|
|
12
|
+
skip: (page - 1) * perPage,
|
|
13
|
+
filterOperator: filterOperator,
|
|
14
|
+
sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
|
|
15
|
+
...(filter && { filter }),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{ items: deepMerge(selector, additionalSelector ?? {}), totalItems: true },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
return response.administrators;
|
|
22
|
+
};
|
|
23
|
+
const onRemove = async (items) => {
|
|
24
|
+
try {
|
|
25
|
+
const ids = items.map((item) => item.id);
|
|
26
|
+
const { deleteAdministrators } = await apiClient('mutation')({
|
|
27
|
+
deleteAdministrators: [
|
|
28
|
+
{ ids },
|
|
29
|
+
{
|
|
30
|
+
message: true,
|
|
31
|
+
result: true,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
return !!deleteAdministrators.length;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return error;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
export const AdminsListPage = () => {
|
|
42
|
+
const { t } = useTranslation('admins');
|
|
43
|
+
return (_jsx(DetailList, { filterFields: [
|
|
44
|
+
{ key: 'firstName', operator: 'StringOperators' },
|
|
45
|
+
{ key: 'emailAddress', operator: 'StringOperators' },
|
|
46
|
+
{ key: 'lastName', operator: 'StringOperators' },
|
|
47
|
+
], detailLinkColumn: "id", searchFields: ['firstName', 'lastName', 'emailAddress'], hideColumns: ['customFields', 'translations', 'user'], additionalColumns: [
|
|
48
|
+
{
|
|
49
|
+
accessorKey: 'role',
|
|
50
|
+
enableSorting: false,
|
|
51
|
+
enableColumnFilter: false,
|
|
52
|
+
header: () => _jsx(TableLabel, { children: t('table.role') }),
|
|
53
|
+
cell: ({ row }) => (_jsx("div", { className: "flex gap-1", children: row.original.user.roles.map((r) => (_jsx(ListBadge, { children: r.description }, r.description))) })),
|
|
54
|
+
},
|
|
55
|
+
], entityName: 'Administrator', route: Routes['admins'], tableId: tableId, fetch: fetch, onRemove: onRemove, createPermissions: [Permission.CreateAdministrator], deletePermissions: [Permission.DeleteAdministrator] }));
|
|
56
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo } from 'react';
|
|
3
|
+
import { EntityCustomFields, CardIcons, CustomCard, DetailViewMarker, Input, useDetailView, useTranslation, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { RolesCard } from "./RolesCard";
|
|
5
|
+
const ADMIN_FORM_KEYS = [
|
|
6
|
+
'CreateAdministratorInput',
|
|
7
|
+
'firstName',
|
|
8
|
+
'lastName',
|
|
9
|
+
'emailAddress',
|
|
10
|
+
'password',
|
|
11
|
+
'roleIds',
|
|
12
|
+
'customFields',
|
|
13
|
+
];
|
|
14
|
+
export const AdminDetailView = () => {
|
|
15
|
+
const { form, entity, fetchEntity, id } = useDetailView('admins-detail-view', ...ADMIN_FORM_KEYS);
|
|
16
|
+
const { base: { setField, state }, } = form;
|
|
17
|
+
const editMode = useMemo(() => !!id, [id]);
|
|
18
|
+
const { t } = useTranslation('admins');
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
(async () => {
|
|
21
|
+
const res = await fetchEntity();
|
|
22
|
+
if (!res)
|
|
23
|
+
return;
|
|
24
|
+
setField('firstName', res.firstName);
|
|
25
|
+
setField('lastName', res.lastName);
|
|
26
|
+
setField('emailAddress', res.emailAddress);
|
|
27
|
+
setField('password', '');
|
|
28
|
+
setField('roleIds', res.user.roles.map((r) => r.id));
|
|
29
|
+
})();
|
|
30
|
+
}, []);
|
|
31
|
+
return (_jsx("main", { className: "my-4", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(CustomCard, { title: t('details.basic.title'), icon: _jsx(CardIcons.basic, {}), children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.firstName'), value: state.firstName?.value ?? undefined, onChange: (e) => setField('firstName', e.target.value), errors: state.firstName?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.lastName'), value: state.lastName?.value ?? undefined, onChange: (e) => setField('lastName', e.target.value), errors: state.lastName?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.emailAddress'), value: state.emailAddress?.value ?? undefined, onChange: (e) => setField('emailAddress', e.target.value), errors: state.emailAddress?.errors, required: true }), _jsx(Input, { wrapperClassName: "basis-full md:basis-1/2 xl:basis-1/4", label: t('details.basic.password'), value: state.password?.value ?? undefined, onChange: (e) => setField('password', e.target.value), errors: state.password?.errors, required: !editMode })] }) }), _jsx(DetailViewMarker, { position: 'admins-detail-view' }), _jsx(EntityCustomFields, { entityName: "administrator", id: id, hideButton: true, onChange: (customFields) => {
|
|
32
|
+
setField('customFields', customFields);
|
|
33
|
+
}, initialValues: entity && 'customFields' in entity ? { customFields: entity.customFields } : { customFields: {} } }), _jsx(RolesCard, { adminRoleIds: state.roleIds?.value ?? undefined, onRolesChange: (e) => setField('roleIds', e), errors: state.roleIds?.errors })] }) }));
|
|
34
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useTranslation, MultipleSelector, apiClient, ErrorMessage, CardIcons, CustomCard, RoleSelector, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { PermissionsTable } from "../../roles/_components/PermissionsTable";
|
|
5
|
+
export const RolesCard = ({ adminRoleIds, onRolesChange, errors }) => {
|
|
6
|
+
const { t } = useTranslation('admins');
|
|
7
|
+
const [allRoles, setAllRoles] = useState([]);
|
|
8
|
+
const currentPermissions = useMemo(() => {
|
|
9
|
+
if (!allRoles.length)
|
|
10
|
+
return;
|
|
11
|
+
return adminRoleIds?.flatMap((id) => allRoles.find((r) => r.id === id)?.permissions ?? []);
|
|
12
|
+
}, [adminRoleIds, allRoles]);
|
|
13
|
+
const fetchAllRoles = useCallback(() => {
|
|
14
|
+
apiClient('query')({
|
|
15
|
+
roles: [{}, { items: RoleSelector }],
|
|
16
|
+
}).then((resp) => {
|
|
17
|
+
setAllRoles(resp.roles.items);
|
|
18
|
+
});
|
|
19
|
+
}, []);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
fetchAllRoles();
|
|
22
|
+
}, [fetchAllRoles]);
|
|
23
|
+
const rolesToOptions = useCallback((roles) => roles?.map((r) => ({ label: r.description, value: r.id })), []);
|
|
24
|
+
const currentRolesOptions = useMemo(() => {
|
|
25
|
+
if (!allRoles.length)
|
|
26
|
+
return undefined;
|
|
27
|
+
else
|
|
28
|
+
return adminRoleIds?.map((id) => ({ value: id, label: allRoles.find((r) => r.id === id).description }));
|
|
29
|
+
}, [allRoles, adminRoleIds]);
|
|
30
|
+
return (_jsxs(CustomCard, { title: t('details.basic.title'), icon: _jsx(CardIcons.permissions, {}), upperRight: _jsx(ErrorMessage, { errors: errors }), children: [_jsx(MultipleSelector, { options: rolesToOptions(allRoles), value: currentRolesOptions, placeholder: t('details.roles.placeholder'), onChange: (options) => onRolesChange(options.map((o) => o.value)), hideClearAllButton: true }), _jsx("div", { className: "mt-4", children: _jsx(PermissionsTable, { currentPermissions: currentPermissions }) })] }));
|
|
31
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { apiClient, DetailList, deepMerge, ListLocations, createDialogFromComponent, Badge, ImageWithPreview, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
|
|
3
|
+
import { Permission, SortOrder } from '@deenruv/admin-types';
|
|
4
|
+
import { UploadAssetDialog } from "./_components/UploadAssetDialog.js";
|
|
5
|
+
import { EditAssetDialog } from "./_components/EditAssetDialog.js";
|
|
6
|
+
const tableId = 'assets-list-view';
|
|
7
|
+
const { selector } = ListLocations[tableId];
|
|
8
|
+
const fetch = async ({ page, perPage, filter, filterOperator, sort }, additionalSelector) => {
|
|
9
|
+
const response = await apiClient('query')({
|
|
10
|
+
assets: [
|
|
11
|
+
{
|
|
12
|
+
options: {
|
|
13
|
+
take: perPage,
|
|
14
|
+
skip: (page - 1) * perPage,
|
|
15
|
+
filterOperator: filterOperator,
|
|
16
|
+
sort: sort ? { [sort.key]: sort.sortDir } : { createdAt: SortOrder.DESC },
|
|
17
|
+
...(filter && { filter }),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{ items: deepMerge(selector, additionalSelector ?? {}), totalItems: true },
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
return response.assets;
|
|
24
|
+
};
|
|
25
|
+
const onRemove = async (items) => {
|
|
26
|
+
try {
|
|
27
|
+
const ids = items.map((item) => item.id);
|
|
28
|
+
const { deleteAssets } = await apiClient('mutation')({
|
|
29
|
+
deleteAssets: [{ input: { assetIds: ids } }, { message: true, result: true }],
|
|
30
|
+
});
|
|
31
|
+
return !!deleteAssets.result.length;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return error;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
export const AssetsListPage = () => {
|
|
38
|
+
const { t } = useTranslation('table');
|
|
39
|
+
return (_jsx(DetailList, { tableId: tableId, detailLinkColumn: "id", searchFields: ['id'], suggestedOrderColumns: { id: 1, preview: 2, tags: 3 }, hideColumns: [
|
|
40
|
+
'fileSize',
|
|
41
|
+
'width',
|
|
42
|
+
'height',
|
|
43
|
+
'focalPoint',
|
|
44
|
+
'mimeType',
|
|
45
|
+
'source',
|
|
46
|
+
'type',
|
|
47
|
+
'customFields',
|
|
48
|
+
'translations',
|
|
49
|
+
], entityName: 'Asset', route: {
|
|
50
|
+
create: async (refetch) => {
|
|
51
|
+
const response = await createDialogFromComponent(UploadAssetDialog, {}, { className: 'max-w-[1200px] h-[700px]' });
|
|
52
|
+
if (response.success) {
|
|
53
|
+
refetch();
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
edit: async (id, row, refetch) => {
|
|
57
|
+
const response = await createDialogFromComponent(EditAssetDialog, row.original, {
|
|
58
|
+
className: 'max-w-[1200px] h-[700px]',
|
|
59
|
+
});
|
|
60
|
+
if (response.success) {
|
|
61
|
+
refetch();
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
}, fetch: fetch, onRemove: onRemove, additionalColumns: [
|
|
65
|
+
{
|
|
66
|
+
accessorKey: 'preview',
|
|
67
|
+
header: () => _jsx(TableLabel, { children: t('columns.image') }),
|
|
68
|
+
cell: ({ row }) => {
|
|
69
|
+
const { name, preview } = row.original;
|
|
70
|
+
return (_jsx("div", { className: "relative", children: _jsx(ImageWithPreview, { src: preview, alt: name, previewClassName: "p-2" }) }));
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
accessorKey: 'tags',
|
|
75
|
+
header: () => _jsx(TableLabel, { children: t('columns.tags') }),
|
|
76
|
+
cell: ({ row }) => {
|
|
77
|
+
const { tags } = row.original;
|
|
78
|
+
return (_jsx("div", { className: "flex flex-wrap gap-1", children: tags.length ? (tags.map((tag, index) => (_jsx(Badge, { variant: "secondary", className: "text-xs", children: tag.value }, index)))) : (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "No Tags" })) }));
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
], createPermissions: [Permission.CreateChannel], deletePermissions: [Permission.DeleteChannel] }));
|
|
82
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Input, Skeleton, Table, TableBody, TableCell, TableRow, Tooltip, apiClient, Tabs, TabsContent, TabsList, TabsTrigger, useServer, EntityCustomFields, useTranslation, TableLabel, } from '@deenruv/react-ui-devkit';
|
|
4
|
+
import { assetsSelector } from "../../../graphql/base";
|
|
5
|
+
import { DeletionResult } from '@deenruv/admin-types';
|
|
6
|
+
import { DialogTrigger } from '@radix-ui/react-dialog';
|
|
7
|
+
import { TooltipContent, TooltipTrigger } from '@radix-ui/react-tooltip';
|
|
8
|
+
import { format } from 'date-fns';
|
|
9
|
+
import { useCallback, useState } from 'react';
|
|
10
|
+
import { toast } from 'sonner';
|
|
11
|
+
import { Copy, ExternalLink, MoreHorizontal, Pencil, Trash } from 'lucide-react';
|
|
12
|
+
export const Asset = ({ asset, onAssetChange }) => {
|
|
13
|
+
const { t } = useTranslation(['common', 'assets']);
|
|
14
|
+
const [open, setOpen] = useState(false);
|
|
15
|
+
const [assetDetails, setAssetDetails] = useState();
|
|
16
|
+
const [assetName, setAssetName] = useState(asset.name);
|
|
17
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
18
|
+
const [activeTab, setActiveTab] = useState('preview');
|
|
19
|
+
const hasEntityCustomFields = !!useServer((p) => p.serverConfig?.entityCustomFields?.find((el) => el.entityName.charAt(0).toLowerCase() + el.entityName.slice(1) === 'asset'))?.customFields.length;
|
|
20
|
+
const tableData = [
|
|
21
|
+
{
|
|
22
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.id', 'ID') }),
|
|
23
|
+
render: assetDetails?.id,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.name', 'Name') }),
|
|
27
|
+
render: _jsx(Input, { value: assetName, onChange: (e) => setAssetName(e.target.value) }),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.fileSize', 'File Size') }),
|
|
31
|
+
render: assetDetails?.fileSize && assetDetails.fileSize / 1000 + 'kB',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.createdAt', 'Created At') }),
|
|
35
|
+
render: assetDetails?.createdAt && format(new Date(assetDetails.createdAt), 'yyyy-MM-dd, HH:ss'),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.size', 'Dimensions') }),
|
|
39
|
+
render: assetDetails?.width && assetDetails?.height ? `${assetDetails.width}px x ${assetDetails.height}px` : '-',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
header: _jsx(TableLabel, { children: t('assets:detailsTable.source', 'Source') }),
|
|
43
|
+
render: assetDetails?.source,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
const onClose = () => {
|
|
47
|
+
setAssetDetails(undefined);
|
|
48
|
+
setOpen(false);
|
|
49
|
+
};
|
|
50
|
+
const onEditName = useCallback(async () => {
|
|
51
|
+
const { updateAsset } = await apiClient('mutation')({
|
|
52
|
+
updateAsset: [{ input: { id: asset.id, name: assetName } }, { name: true }],
|
|
53
|
+
});
|
|
54
|
+
if (updateAsset.name) {
|
|
55
|
+
toast.success(t('assets:editSuccess', 'Asset updated successfully'));
|
|
56
|
+
onAssetChange();
|
|
57
|
+
onClose();
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
toast.error(t('assets:editFail', 'Failed to update asset'));
|
|
61
|
+
}, [assetName, asset.id, onAssetChange, onClose, t]);
|
|
62
|
+
const onDelete = useCallback(async () => {
|
|
63
|
+
const { deleteAsset } = await apiClient('mutation')({
|
|
64
|
+
deleteAsset: [{ input: { assetId: asset.id } }, { message: true, result: true }],
|
|
65
|
+
});
|
|
66
|
+
if (deleteAsset.result === DeletionResult.DELETED) {
|
|
67
|
+
toast.success(t('assets:deleteSuccess', 'Asset deleted successfully'));
|
|
68
|
+
onAssetChange();
|
|
69
|
+
onClose();
|
|
70
|
+
}
|
|
71
|
+
else
|
|
72
|
+
toast.error(t('assets:deleteFail', 'Failed to delete asset') + ': ' + deleteAsset.message);
|
|
73
|
+
}, [asset.id, onAssetChange, onClose, t]);
|
|
74
|
+
const getAsset = async () => {
|
|
75
|
+
const response = await apiClient('query')({
|
|
76
|
+
asset: [
|
|
77
|
+
{
|
|
78
|
+
id: asset.id,
|
|
79
|
+
},
|
|
80
|
+
assetsSelector,
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
if (response.asset)
|
|
84
|
+
setAssetDetails(response.asset);
|
|
85
|
+
};
|
|
86
|
+
const copyAssetUrl = () => {
|
|
87
|
+
navigator.clipboard.writeText(asset.preview);
|
|
88
|
+
toast.success(t('assets:urlCopied', 'Asset URL copied to clipboard'));
|
|
89
|
+
};
|
|
90
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs("div", { className: "bg-background group relative h-52 w-40 overflow-hidden rounded-lg border transition-all hover:shadow-md", onClick: () => getAsset(), children: [_jsx("div", { className: "absolute inset-0 bg-black/0 transition-colors duration-200 group-hover:bg-black/5" }), _jsx("div", { className: "absolute right-2 top-2 z-10", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, onClick: (e) => e.stopPropagation(), children: _jsxs(Button, { size: "sm", variant: "ghost", className: "bg-background/80 size-8 rounded-full p-0 opacity-0 shadow-sm backdrop-blur-sm group-hover:opacity-100", children: [_jsx(MoreHorizontal, { size: 16 }), _jsx("span", { className: "sr-only", children: "Open menu" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-[160px]", children: [_jsxs(DropdownMenuItem, { onClick: (e) => {
|
|
91
|
+
e.stopPropagation();
|
|
92
|
+
copyAssetUrl();
|
|
93
|
+
}, className: "gap-2", children: [_jsx(Copy, { size: 14 }), _jsx("span", { children: t('common:copyURL', 'Copy URL') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
|
|
94
|
+
e.stopPropagation();
|
|
95
|
+
window.open(asset.preview, '_blank');
|
|
96
|
+
}, className: "gap-2", children: [_jsx(ExternalLink, { size: 14 }), _jsx("span", { children: t('common:open', 'Open') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
getAsset();
|
|
99
|
+
setOpen(true);
|
|
100
|
+
}, className: "gap-2", children: [_jsx(Pencil, { size: 14 }), _jsx("span", { children: t('common:edit', 'Edit') })] }), _jsxs(DropdownMenuItem, { onClick: (e) => {
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
getAsset();
|
|
103
|
+
onDelete();
|
|
104
|
+
}, className: "text-destructive gap-2", children: [_jsx(Trash, { size: 14 }), _jsx("span", { children: t('common:delete', 'Delete') })] })] })] }) }), _jsxs("div", { className: "bg-muted/30 aspect-square", children: [isLoading && _jsx(Skeleton, { className: "size-full" }), _jsx("img", { src: `${asset.preview}?preset=tile`, alt: asset.name, className: "size-full object-cover transition-all duration-300", style: { opacity: isLoading ? 0 : 1 }, onLoad: () => setIsLoading(false), onError: () => setIsLoading(false) })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { className: "block w-full truncate p-3 text-left text-sm font-medium", title: asset.name, children: asset.name }), _jsx(TooltipContent, { className: "bg-popover text-popover-foreground z-50 rounded-md border px-3 py-1.5 text-sm shadow-md", children: asset.name })] })] }) }), _jsxs(DialogContent, { className: "max-w-5xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { className: "text-xl", children: asset.name }) }), _jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "mt-2", children: [_jsxs(TabsList, { className: "grid w-full max-w-md grid-cols-2", children: [_jsx(TabsTrigger, { value: "preview", children: t('common:preview') }), _jsx(TabsTrigger, { value: "details", children: t('common:details') })] }), _jsx(TabsContent, { value: "preview", className: "space-y-4", children: _jsxs("div", { className: "relative flex h-[500px] items-center justify-center overflow-hidden rounded-lg p-4", children: [_jsx("div", { className: "absolute inset-0 bg-[#f0f0f0]", style: {
|
|
105
|
+
backgroundImage: `
|
|
106
|
+
linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
|
|
107
|
+
linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
|
|
108
|
+
linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
|
|
109
|
+
linear-gradient(-45deg, transparent 75%, #e0e0e0 75%)
|
|
110
|
+
`,
|
|
111
|
+
backgroundSize: '20px 20px',
|
|
112
|
+
backgroundPosition: '0 0, 0 10px, 10px -10px, -10px 0px',
|
|
113
|
+
} }), _jsx("div", { className: "border-border pointer-events-none absolute inset-0 rounded-lg border" }), _jsx("img", { src: assetDetails?.source ? `${assetDetails.source}?preset=medium` : asset.preview, alt: asset.name, className: "relative z-10 max-h-full max-w-full rounded object-contain shadow-md" })] }) }), _jsx(TabsContent, { value: "details", className: "mt-4", children: _jsxs("div", { className: "grid h-[492px] gap-6 md:grid-cols-2", children: [_jsx("div", { className: "overflow-hidden rounded-lg border", children: _jsx(Table, { children: _jsx(TableBody, { children: tableData.map((d, i) => (_jsxs(TableRow, { className: i % 2 === 0 ? 'bg-muted/30' : '', children: [_jsx(TableCell, { className: "w-1/3 font-medium", children: d.header }), _jsx(TableCell, { className: "break-all", children: d.render })] }, i))) }) }) }), hasEntityCustomFields && (_jsxs("div", { children: [_jsxs("h3", { className: "mb-3 flex items-center gap-2 text-lg font-medium", children: [_jsx(Pencil, { size: 16 }), t('common:customFields')] }), _jsx("div", { className: "rounded-lg border p-4", children: _jsx(EntityCustomFields, { entityName: "asset", id: asset?.id }) })] }))] }) })] }), _jsxs("div", { className: "mt-4 flex justify-between", children: [_jsx(Button, { variant: "destructive", onClick: () => onDelete(), children: t('assets:delete', 'Delete') }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { variant: "outline", onClick: () => onClose(), children: t('common:cancel', 'Cancel') }), _jsx(Button, { onClick: () => onEditName(), children: t('common:save', 'Save') })] })] })] })] }));
|
|
114
|
+
};
|