diff --git a/src/components/modals/CreateStoreModal-styles.tsx b/src/components/modals/CreateStoreModal-styles.tsx index 890728e..bcff787 100644 --- a/src/components/modals/CreateStoreModal-styles.tsx +++ b/src/components/modals/CreateStoreModal-styles.tsx @@ -195,3 +195,52 @@ export const DownloadArrrWalletIcon = styled(DownloadSVG)(({ theme }) => ({ cursor: "pointer", }, })); + +export const AdvancedSettingsBox = styled(Box)(({ theme }) => ({ + display: "flex", + flexDirection: "row", + alignItems: "center", + gap: "10px", + padding: "5px 10px", + borderRadius: "5px", + backgroundColor: theme.palette.background.paper, + "& .MuiTypography-root": { + fontFamily: "Karla", + fontSize: "20px", + fontWeight: 300, + letterSpacing: "0.2px", + } +})); + +export const EditStoreButtonsRow = styled(Box)({ + display: "flex", + alignItems: "center", + gap: "10px", +}); + +export const CreateNewDataContainerRow = styled(Box)({ + display: "flex", + alignItems: "center", +}); + +export const CreateNewDataContainerButton = styled(Button)(({ theme }) => ({ + backgroundColor: "#d43232", + textTransform: "none", + fontFamily: "Raleway", + fontWeight: 300, + gap: "5px", + fontSize: "17px", + borderRadius: "5px", + border: "none", + color: "white", + padding: "5px 15px", + transition: "all 0.3s ease-in-out", + boxShadow: + "rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;", + "&:hover": { + cursor: "pointer", + backgroundColor: "#a72727", + boxShadow: + "rgba(50, 50, 93, 0.35) 0px 3px 5px -1px, rgba(0, 0, 0, 0.4) 0px 2px 3px -1px;" + } +})); \ No newline at end of file diff --git a/src/components/modals/EditStoreModal.tsx b/src/components/modals/EditStoreModal.tsx index f475d1e..f09e852 100644 --- a/src/components/modals/EditStoreModal.tsx +++ b/src/components/modals/EditStoreModal.tsx @@ -7,9 +7,15 @@ import { IconButton, Tooltip, Zoom, + Box, } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; -import { toggleCreateStoreModal } from "../../state/features/globalSlice"; +import { + toggleCreateStoreModal, + setDataContainer, + resetListProducts, + resetProducts +} from "../../state/features/globalSlice"; import { RootState } from "../../state/store"; import { AddLogoButton, @@ -25,6 +31,10 @@ import { ModalTitle, StoreLogoPreview, TimesIcon, + AdvancedSettingsBox, + EditStoreButtonsRow, + CreateNewDataContainerRow, + CreateNewDataContainerButton, } from "./CreateStoreModal-styles"; import ImageUploader from "../common/ImageUploader"; import { @@ -37,6 +47,11 @@ import { import { supportedCoinsArray } from "../../constants/supported-coins"; import { QortalSVG } from "../../assets/svgs/QortalSVG"; import { ARRRSVG } from "../../assets/svgs/ARRRSVG"; +import { ReusableModal } from "./ReusableModal"; +import { DATA_CONTAINER_BASE, STORE_BASE } from "../../constants/identifiers"; +import { objectToBase64 } from "../../utils/toBase64"; +import { ShortDataContainer } from "../../wrappers/GlobalWrapper"; +import { setNotification } from "../../state/features/notificationsSlice"; interface ForeignCoins { [key: string]: string; @@ -62,6 +77,7 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { const currentStore = useSelector( (state: RootState) => state.global.currentStore ); + const user = useSelector((state: RootState) => state.auth.user); const storeId = useSelector((state: RootState) => state.store.storeId); @@ -76,6 +92,10 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { >(["QORT"]); const [qortWalletAddress, setQortWalletAddress] = useState(""); const [arrrWalletAddress, setArrrWalletAddress] = useState(""); + const [showAdvancedSettings, setShowAdvancedSettings] = + useState(false); + const [showCreateNewDataContainerModal, setShowCreateNewDataContainerModal] = + useState(false); const theme = useTheme(); @@ -88,11 +108,14 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { } const foreignCoins: ForeignCoins = { - ARRR: arrrWalletAddress - } - supportedCoinsSelected.filter((coin)=> coin !== 'QORT').forEach((item: string)=> { - if(!foreignCoins[item]) throw new Error(`Please add a ${item} address`) - }) + ARRR: arrrWalletAddress, + }; + supportedCoinsSelected + .filter(coin => coin !== "QORT") + .forEach((item: string) => { + if (!foreignCoins[item]) + throw new Error(`Please add a ${item} address`); + }); await onPublish({ title, description, @@ -100,9 +123,9 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { location, logo, foreignCoins: { - ARRR: arrrWalletAddress + ARRR: arrrWalletAddress, }, - supportedCoins: supportedCoinsSelected + supportedCoins: supportedCoinsSelected, }); handleClose(); } catch (error: any) { @@ -117,8 +140,8 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { setLogo(currentStore?.logo || null); setLocation(currentStore?.location || ""); setShipsTo(currentStore?.shipsTo || ""); - setSupportedCoinsSelected(currentStore?.supportedCoins || ['QORT']) - setArrrWalletAddress(currentStore?.foreignCoins?.ARRR || "") + setSupportedCoinsSelected(currentStore?.supportedCoins || ["QORT"]); + setArrrWalletAddress(currentStore?.foreignCoins?.ARRR || ""); } }, [currentStore, storeId, open]); @@ -130,8 +153,9 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { setLogo(null); setLocation(""); setShipsTo(""); - setArrrWalletAddress("") - setSupportedCoinsSelected(["QORT"]) + setArrrWalletAddress(""); + setSupportedCoinsSelected(["QORT"]); + setShowAdvancedSettings(false); dispatch(toggleCreateStoreModal(false)); onClose(); }; @@ -145,19 +169,82 @@ const MyModal: React.FC = ({ open, onClose, onPublish }) => { setSupportedCoinsSelected(prevChips => prevChips.filter(c => c !== chip)); }; - const importAddress = async (coin: string)=> { + const importAddress = async (coin: string) => { try { const res = await qortalRequest({ - action: 'GET_USER_WALLET', - coin - }) - if(res?.address){ - setArrrWalletAddress(res.address) + action: "GET_USER_WALLET", + coin, + }); + if (res?.address) { + setArrrWalletAddress(res.address); } } catch (error) { - + console.error(error); } - } + }; + + // Recreate Shop Data + + const handleRecreateShopData = async () => { + if (!currentStore || !user?.name) { + dispatch( + setNotification({ + msg: "Error! Missing shop data or name", + alertType: "error", + }) + ); + return; + } + try { + const shortStoreId = currentStore?.shortStoreId; + const dataContainer: ShortDataContainer = { + storeId: currentStore.id, + shortStoreId: shortStoreId, + owner: user.name, + products: {}, + }; + const dataContainerToBase64 = await objectToBase64(dataContainer); + + const dataContainerCreated = await qortalRequest({ + action: "PUBLISH_QDN_RESOURCE", + name: user?.name, + service: "DOCUMENT", + data64: dataContainerToBase64, + identifier: `${currentStore.id}-${DATA_CONTAINER_BASE}`, + }); + console.log({ dataContainerCreated }); + if (dataContainerCreated && !dataContainerCreated.error) { + dispatch( + setDataContainer({ + ...dataContainer, + id: `${currentStore.id}-${DATA_CONTAINER_BASE}`, + }) + ); + dispatch(resetListProducts()); + dispatch(resetProducts()); + dispatch( + setNotification({ + msg: "Data Container Created!", + alertType: "success", + }) + ); + onClose(); + } + setShowCreateNewDataContainerModal(false); + setShowAdvancedSettings(false); + } catch (error) { + console.error(error); + navigate("/"); + dispatch( + setNotification({ + msg: "Error when creating the data container. Please try again!", + alertType: "error", + }) + ); + dispatch(updateRecentlyVisitedStoreId("")); + dispatch(clearDataCotainer()); + } + }; return ( = ({ open, onClose, onPublish }) => { arrow={true} title="Import your ARRR Wallet Address from your current account" > - importAddress('ARRR')}> + importAddress("ARRR")} + > = ({ open, onClose, onPublish }) => { /> )} /> + {showAdvancedSettings && ( + { + setShowCreateNewDataContainerModal(true); + }} + > + + Recreate Shop Data + + + )} {errorMessage && ( {errorMessage} )} - - - Cancel - - - Edit Shop - + + + Advanced Settings + setShowAdvancedSettings(!showAdvancedSettings)} + /> + + + + Cancel + + + Edit Shop + + + + + Warning! ⚠️ Are you sure you want to recreate your shop's data? This + will clear all your shop's products. This should only be done as a + last resort if you cannot access your product manager or if you are + experiencing issues products displaying properly. + + + { + setShowCreateNewDataContainerModal(false); + }} + > + Cancel + + + Recreate Shop Data + + + ); diff --git a/src/pages/Store/Store/Store.tsx b/src/pages/Store/Store/Store.tsx index c055e25..c191de7 100644 --- a/src/pages/Store/Store/Store.tsx +++ b/src/pages/Store/Store/Store.tsx @@ -217,13 +217,17 @@ export const Store = () => { const switchCoin = async ()=> { dispatch(setIsLoadingGlobal(true)); - await calculateARRRExchangeRate() dispatch(setIsLoadingGlobal(false)); - - } + // If shop's datacontainer changes, and listProducts becomes empty, clear local products array + useEffect(() => { + if (username === user?.name && userOwnDataContainer && Object.keys(userOwnDataContainer.products).length === 0) { + setProducts([]); + } + }, [userOwnDataContainer]); + useEffect(()=> { if(preferredCoin === CoinFilter.arrr && storeToUse?.supportedCoins?.includes(CoinFilter.arrr)){ switchCoin() diff --git a/src/wrappers/GlobalWrapper.tsx b/src/wrappers/GlobalWrapper.tsx index fad1b02..9454f7f 100644 --- a/src/wrappers/GlobalWrapper.tsx +++ b/src/wrappers/GlobalWrapper.tsx @@ -53,7 +53,8 @@ interface Props { children: React.ReactNode; setTheme: (val: string) => void; } -interface ShortDataContainer { + +export interface ShortDataContainer { storeId: string; shortStoreId: string; owner: string;