diff --git a/wondrous-bot-admin/public/images/banner-images/leaderboard-banner.png b/wondrous-bot-admin/public/images/banner-images/leaderboard-banner.png new file mode 100644 index 0000000000..e275a0daa4 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/leaderboard-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/leaderboard-circle.png b/wondrous-bot-admin/public/images/banner-images/leaderboard-circle.png new file mode 100644 index 0000000000..ad99936dc6 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/leaderboard-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/my-level-banner.png b/wondrous-bot-admin/public/images/banner-images/my-level-banner.png new file mode 100644 index 0000000000..cfa7b08119 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/my-level-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/my-level-circle.png b/wondrous-bot-admin/public/images/banner-images/my-level-circle.png new file mode 100644 index 0000000000..33bfb7e17d Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/my-level-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/my-purchases-banner.png b/wondrous-bot-admin/public/images/banner-images/my-purchases-banner.png new file mode 100644 index 0000000000..c3256cfc62 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/my-purchases-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/my-purchases-circle.png b/wondrous-bot-admin/public/images/banner-images/my-purchases-circle.png new file mode 100644 index 0000000000..8eb363266a Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/my-purchases-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/onboard-me-banner.png b/wondrous-bot-admin/public/images/banner-images/onboard-me-banner.png new file mode 100644 index 0000000000..f687753cd4 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/onboard-me-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/onboard-me-circle.png b/wondrous-bot-admin/public/images/banner-images/onboard-me-circle.png new file mode 100644 index 0000000000..10b28fa086 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/onboard-me-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/quest-banner.png b/wondrous-bot-admin/public/images/banner-images/quest-banner.png new file mode 100644 index 0000000000..31216c65f7 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/quest-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/quest-circle.png b/wondrous-bot-admin/public/images/banner-images/quest-circle.png new file mode 100644 index 0000000000..fafe237d73 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/quest-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/store-banner.png b/wondrous-bot-admin/public/images/banner-images/store-banner.png new file mode 100644 index 0000000000..eb51f1548e Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/store-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/store-circle.png b/wondrous-bot-admin/public/images/banner-images/store-circle.png new file mode 100644 index 0000000000..1967b31ef1 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/store-circle.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/sub-banner.png b/wondrous-bot-admin/public/images/banner-images/sub-banner.png new file mode 100644 index 0000000000..cc2758a51a Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/sub-banner.png differ diff --git a/wondrous-bot-admin/public/images/banner-images/sub-circle.png b/wondrous-bot-admin/public/images/banner-images/sub-circle.png new file mode 100644 index 0000000000..c1dd8c39b6 Binary files /dev/null and b/wondrous-bot-admin/public/images/banner-images/sub-circle.png differ diff --git a/wondrous-bot-admin/src/App.tsx b/wondrous-bot-admin/src/App.tsx index 8b83a8d12a..3c33405769 100644 --- a/wondrous-bot-admin/src/App.tsx +++ b/wondrous-bot-admin/src/App.tsx @@ -24,6 +24,7 @@ import { WonderWeb3Provider } from "utils/context/WonderWeb3Context"; import SettingsPage from "pages/settings"; import TeamSettingsPage from "pages/settings/team"; import BillingPage from "pages/settings/billing"; +import CustomizeBannersPage from "pages/settings/customize-banners"; import NotificationSettingsPage from "pages/settings/notification"; import WalletConnectPage from "pages/wallet/connect"; import OnboardingPage from "pages/onboarding"; @@ -119,6 +120,10 @@ const router = createBrowserRouter([ path: "/settings/nft", element: , }, + { + path: "/settings/customize-banners", + element: , + }, { path: "/", element: , diff --git a/wondrous-bot-admin/src/components/ImageUpload/AvatarEditor/index.tsx b/wondrous-bot-admin/src/components/ImageUpload/AvatarEditor/index.tsx index 8c3b27fcec..e4e3752f18 100644 --- a/wondrous-bot-admin/src/components/ImageUpload/AvatarEditor/index.tsx +++ b/wondrous-bot-admin/src/components/ImageUpload/AvatarEditor/index.tsx @@ -1,20 +1,17 @@ -import { useEffect, useRef, useState } from 'react'; - -import max from 'lodash/max'; -import DefaultAvatarEditor from 'react-avatar-editor'; - -import { AvatarEditorTypes } from 'types/assets'; -import { - ButtonIconWrapper, - SharedSecondaryButton, -} from 'components/Shared/styles'; -import Modal from 'components/Shared/Modal'; -import { Box, Grid } from '@mui/material'; -import { pinkColors } from 'utils/theme/colors'; -import ReplaceIcon from 'components/Icons/ReplaceIcon'; -import DeleteIcon from 'components/Icons/Delete'; -import { Label } from 'components/CreateTemplate/styles'; -import { ZoomIn, ZoomOut } from '@mui/icons-material'; +import { useEffect, useRef, useState } from "react"; + +import max from "lodash/max"; +import DefaultAvatarEditor from "react-avatar-editor"; + +import { AvatarEditorTypes } from "types/assets"; +import { ButtonIconWrapper, SharedSecondaryButton } from "components/Shared/styles"; +import Modal from "components/Shared/Modal"; +import { Box, Grid } from "@mui/material"; +import { pinkColors } from "utils/theme/colors"; +import ReplaceIcon from "components/Icons/ReplaceIcon"; +import DeleteIcon from "components/Icons/Delete"; +import { Label } from "components/CreateTemplate/styles"; +import { ZoomIn, ZoomOut } from "@mui/icons-material"; type Props = { originalImage: string | File; @@ -25,7 +22,7 @@ type Props = { clearInput: () => void; imageType: AvatarEditorTypes; title: string; - recommendDateionText: string; + recommendDateionText?: string; }; interface ImageViewerObjectProps { @@ -34,6 +31,29 @@ interface ImageViewerObjectProps { borderRadius: number; } +const IMAGE_VIEWER_SIZE: Record = { + HEADER_IMAGE: { + width: 550, + height: 78, + borderRadius: 0, + }, + ICON_IMAGE: { + width: 250, + height: 250, + borderRadius: 250, + }, + BANNER_IMAGE: { + width: 640, + height: 140, + borderRadius: 0, + }, + BANNER_LOGO_IMAGE: { + width: 400, + height: 400, + borderRadius: 0, + }, +}; + const AvatarEditor = ({ originalImage, open, @@ -50,18 +70,7 @@ const AvatarEditor = ({ const [angle, setAngle] = useState(0); const editorRef = useRef(null); - const imageViewerSize: Record = { - HEADER_IMAGE: { - width: 550, - height: 78, - borderRadius: 0, - }, - ICON_IMAGE: { - width: 250, - height: 250, - borderRadius: 250, - }, - }; + const currentImageViewerSize = IMAGE_VIEWER_SIZE[imageType]; useEffect(() => { setScale(1.0); @@ -83,27 +92,27 @@ const AvatarEditor = ({ // Event handlers const onAction = (action) => { switch (action) { - case 'zoom_in': + case "zoom_in": setScale(scale + defaultZoomScale); break; - case 'zoom_out': + case "zoom_out": setScale(max([scale - defaultZoomScale, maxZoomOut]) || maxZoomOut); break; - case 'rotate_right': + case "rotate_right": setAngle(angle + defaultRotateAngle); break; - case 'rotate_left': + case "rotate_left": setAngle(angle - defaultRotateAngle); break; - case 'crop': + case "crop": break; default: - throw new Error('Unknown action'); + throw new Error("Unknown action"); } }; @@ -125,95 +134,89 @@ const AvatarEditor = ({ const file = new File([blob], `profile-pic.${blob.type.substring(6)}`, { type: blob.type, }); + console.log("fetch file", file); onSave([file]); }); }; + const maxWidth = + currentImageViewerSize.width > currentImageViewerSize.height + ? currentImageViewerSize.width * 1.5 + : currentImageViewerSize.width * 2; + return ( Cancel } - footerRight={ - - Confirm image - - } + footerRight={Confirm image} + modalFooterStyle={{ + gap: "8px", + }} > - - + + - - - + + + Replace image - + Delete image - - onAction('zoom_in')}> + + onAction("zoom_in")}> - onAction('zoom_out')}> + onAction("zoom_out")}> - - + + ); }; -export const AVATAR_EDITOR_TYPES: Record = - { - HEADER_IMAGE: 'HEADER_IMAGE', - ICON_IMAGE: 'ICON_IMAGE', - }; +export const AVATAR_EDITOR_TYPES: Record = { + HEADER_IMAGE: "HEADER_IMAGE", + ICON_IMAGE: "ICON_IMAGE", + BANNER_IMAGE: "BANNER_IMAGE", + BANNER_LOGO_IMAGE: "BANNER_LOGO_IMAGE", +}; export default AvatarEditor; diff --git a/wondrous-bot-admin/src/components/Settings/CustomizeBanners/index.tsx b/wondrous-bot-admin/src/components/Settings/CustomizeBanners/index.tsx new file mode 100644 index 0000000000..ff2335b7ab --- /dev/null +++ b/wondrous-bot-admin/src/components/Settings/CustomizeBanners/index.tsx @@ -0,0 +1,488 @@ +import { Box } from "@mui/material"; +import DeleteIcon from "components/Icons/Delete"; +import { useContext, useRef, useState } from "react"; +import { ButtonIconWrapper } from "components/Shared/styles"; +import ReplaceIcon from "components/Icons/ReplaceIcon"; +import { + CommandBannerContainer, + HeaderText, + HeaderContainer, + BannerUploadText, + BannerUploadButtonContainer, + BannerUploadTextButtonContainer, + TopImageTextButtonContainer, + TopImageText, + CommandBannerUploadContainer, + TopImageSectionContainer, + TopImageImageButtonContainer, + TopImageButtonContainer, + SectionDivider, + CustomizeBannersContainer, + BannerUploadHeader, + BannerUploadContainer, + CommandsContainer, + HeaderContainerTooltipContent, + BannerUploadImageContainer, + TopImageContainer, + ButtonInputContainer, +} from "./styles"; +import { StyledInformationTooltip } from "components/Shared/Tooltip"; +import InformationTooltip from "components/Icons/information.svg"; +import { DELETE_ORG_BANNER, UPSERT_ORG_CUSTOM_BANNER } from "graphql/mutations/orgAsset"; +import { useMutation, useQuery } from "@apollo/client"; +import GlobalContext from "utils/context/GlobalContext"; +import { transformAndUploadMedia } from "utils/media"; +import { GET_ORG_CUSTOM_ASSETS } from "graphql/queries/orgAsset"; +import SafeImage from "components/SafeImage"; +import useAlerts, { useSubscriptionPaywall } from "utils/hooks"; +import { useNavigate } from "react-router-dom"; +import AvatarEditor, { AVATAR_EDITOR_TYPES } from "components/ImageUpload/AvatarEditor"; + +const EXEMPTED_ORG_IDS = ["911445364593262592", "844677430694510634", "1192224585215639572"]; + +const ORG_ASSET_PURPOSE = { + questBanner: "quest_banner", + levelBanner: "level_banner", + leaderboardBanner: "leaderboard_banner", + startQuestBanner: "start_quest_banner", + mySubmissionBanner: "my_submission_banner", + storeBanner: "store_banner", + myPurchasesBanner: "my_purchases_banner", + onboardMeBanner: "onboard_me_banner", + referralBanner: "referral_banner", + questLogo: "quest_logo", + levelLogo: "level_logo", + leaderboardLogo: "leaderboard_logo", + mySubmissionLogo: "my_submission_logo", + storeLogo: "store_logo", + myPurchasesLogo: "my_purchases_logo", + onboardMeLogo: "onboard_me_logo", + referralLogo: "referral_logo", +}; + +const commandBanners: { + title: string; + tooltip: string; + banner: { + purpose: (typeof ORG_ASSET_PURPOSE)[keyof typeof ORG_ASSET_PURPOSE]; + image: string; + }; + logo: { + purpose: (typeof ORG_ASSET_PURPOSE)[keyof typeof ORG_ASSET_PURPOSE]; + image: string; + }; +}[] = [ + { + title: "Quests", + tooltip: "Quests are a series of tasks that users can complete to earn rewards.", + banner: { + purpose: ORG_ASSET_PURPOSE.questBanner, + image: "/images/banner-images/quest-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.questLogo, + image: "/images/banner-images/quest-circle.png", + }, + }, + + { + title: "My Submissions", + tooltip: "View all of your submissions and their statuses.", + banner: { + purpose: ORG_ASSET_PURPOSE.mySubmissionBanner, + image: "/images/banner-images/sub-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.mySubmissionLogo, + image: "/images/banner-images/sub-circle.png", + }, + }, + { + title: "My Level", + tooltip: "View your current level and experience points.", + banner: { + purpose: ORG_ASSET_PURPOSE.levelBanner, + image: "/images/banner-images/my-level-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.levelLogo, + image: "/images/banner-images/my-level-circle.png", + }, + }, + { + title: "Leaderboard", + tooltip: "View the top users and their levels.", + banner: { + purpose: ORG_ASSET_PURPOSE.leaderboardBanner, + image: "/images/banner-images/leaderboard-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.leaderboardLogo, + image: "/images/banner-images/leaderboard-circle.png", + }, + }, + { + title: "Store", + tooltip: "View all of the items available for purchase.", + banner: { + purpose: ORG_ASSET_PURPOSE.storeBanner, + image: "/images/banner-images/store-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.storeLogo, + image: "/images/banner-images/store-circle.png", + }, + }, + { + title: "My Purchases", + tooltip: "View all of your purchases.", + banner: { + purpose: ORG_ASSET_PURPOSE.myPurchasesBanner, + image: "/images/banner-images/my-purchases-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.myPurchasesLogo, + image: "/images/banner-images/my-purchases-circle.png", + }, + }, + { + title: "Onboard Me", + tooltip: "Complete the onboarding process.", + banner: { + purpose: ORG_ASSET_PURPOSE.onboardMeBanner, + image: "/images/banner-images/onboard-me-banner.png", + }, + logo: { + purpose: ORG_ASSET_PURPOSE.onboardMeLogo, + image: "/images/banner-images/onboard-me-circle.png", + }, + }, +]; + +const CommandBanner = ({ + baseBanner, + customBanner, + handleReplaceImage, + handleDeleteImage, + setSelectedAvatarProps, + setTempImage, + handleResetAvatarImage, +}) => { + const { title, tooltip, banner, logo } = baseBanner; + const bannerImageInputField = useRef(null); + const topImageInputField = useRef(null); + + const customBannerImage = customBanner?.find((customBanner) => customBanner.purpose === banner.purpose); + const customTopImage = customBanner?.find((customBanner) => customBanner.purpose === logo.purpose); + const customBannerImageUrl = customBannerImage?.publicUrl; + const customTopImageUrl = customTopImage?.publicUrl; + + const handleDeleteBannerImage = async () => { + handleDeleteImage({ + assetId: customBannerImage?.id, + imageInputField: bannerImageInputField, + }); + }; + + const handleDeleteTopImage = async () => { + handleDeleteImage({ + assetId: customTopImage?.id, + imageInputField: topImageInputField, + }); + }; + + const handleOnSave = async ({ file, replaceImageCallback }) => { + if (!file) { + handleResetAvatarImage(); + return; + } + replaceImageCallback(); + handleResetAvatarImage(); + }; + + const handleSetBannerImageAvatarProps = () => { + if (bannerImageInputField.current.value === "") { + bannerImageInputField.current.click(); + return; + } + setSelectedAvatarProps({ + originalImage: customBannerImageUrl || banner.image, + open: true, + openSelectFile: () => bannerImageInputField.current.click(), + imageType: AVATAR_EDITOR_TYPES.BANNER_IMAGE, + onSave: async (file: File) => + handleOnSave({ + file, + replaceImageCallback: async () => + await handleReplaceImage({ file, purpose: banner.purpose, imageInputField: bannerImageInputField }), + }), + onCancel: () => (bannerImageInputField.current.value = ""), + }); + }; + + const handleBannerImageOnChange = (e) => { + setTempImage(e.target.files[0]); + handleSetBannerImageAvatarProps(); + }; + + const handleSetTopImageAvatarProps = (logo) => { + if (topImageInputField.current.value === "") { + topImageInputField.current.click(); + return; + } + setSelectedAvatarProps({ + originalImage: customTopImageUrl || logo.image, + open: true, + openSelectFile: () => topImageInputField.current.click(), + imageType: AVATAR_EDITOR_TYPES.BANNER_LOGO_IMAGE, + onSave: async (file: File) => + handleOnSave({ + file, + replaceImageCallback: async () => + await handleReplaceImage({ file, purpose: logo.purpose, imageInputField: topImageInputField }), + }), + onCancel: () => (topImageInputField.current.value = ""), + }); + }; + + const handleSetTopImageOnChange = (e) => { + setTempImage(e.target.files[0]); + handleSetTopImageAvatarProps(logo); + }; + + return ( + + + + + /{title} + + information + + + + + + + Banner + + {customBannerImageUrl ? ( + + ) : ( + {`${title} + )} + + + Optimal size: 640 x 140px + + + + + + + + + + + + + + + + Top Image + + + {customTopImageUrl ? ( + + ) : ( + {`${title} + )} + + + 400 x 400px + + + + + + + + + + + + + + + + + ); +}; + +const CustomizeBanners = () => { + const navigate = useNavigate(); + const { activeOrg } = useContext(GlobalContext); + const { isLoading, isPremiumPlan, isEcosystemPlan, setPaywall, setPaywallMessage, setOnCancel, setCanBeClosed } = + useSubscriptionPaywall(); + const { data } = useQuery(GET_ORG_CUSTOM_ASSETS, { + variables: { + orgId: activeOrg?.id, + }, + skip: !activeOrg?.id, + }); + const { setSnackbarAlertMessage, setSnackbarAlertOpen, setSnackbarAlertSeverity, showError } = useAlerts(); + const [updateBanner] = useMutation(UPSERT_ORG_CUSTOM_BANNER); + const [deleteBanner] = useMutation(DELETE_ORG_BANNER); + + const defaultAvatarProps = { + originalImage: null, + open: false, + onCancel: () => {}, + }; + const [selectedAvatarProps, setSelectedAvatarProps] = useState(defaultAvatarProps); + const [tempImage, setTempImage] = useState(null); + const handleResetAvatarImage = () => { + selectedAvatarProps?.onCancel(); + setTempImage(null); + setSelectedAvatarProps(defaultAvatarProps); + }; + + if (isLoading || !(isPremiumPlan || isEcosystemPlan) || EXEMPTED_ORG_IDS.includes(activeOrg?.id)) { + setOnCancel(() => { + return () => { + setPaywall(false); + setPaywallMessage(""); + setOnCancel(null); + setCanBeClosed(true); + navigate(-1); + }; + }); + setPaywallMessage("Customize Banners"); + setPaywall(true); + return null; + } + + if (isEcosystemPlan || isPremiumPlan) { + setPaywall(false); + } + + const handleReplaceImage = async ({ file, purpose, imageInputField }) => { + console.log("purpose", purpose); + const image = file[0]; + + if (!image || !image.type.includes("image")) { + showError("Invalid file type"); + return; + } + + const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB + if (image.size > MAX_FILE_SIZE) { + showError("File size should be less than 5MB"); + return; + } + + const { filename } = await transformAndUploadMedia({ + file: image, + }); + + await updateBanner({ + variables: { + input: { + orgId: activeOrg?.id, + purpose, + mediaUpload: { + uploadSlug: filename, + }, + }, + }, + refetchQueries: [GET_ORG_CUSTOM_ASSETS], + onCompleted: () => { + imageInputField.current.value = ""; + setSnackbarAlertSeverity("success"); + setSnackbarAlertMessage("Updated successfully"); + setSnackbarAlertOpen(true); + }, + onError: () => { + showError("Error updating image"); + }, + }); + }; + + const handleDeleteImage = async ({ assetId, imageInputField }) => { + const onCompleted = () => { + imageInputField.current.value = ""; + setSnackbarAlertSeverity("success"); + setSnackbarAlertMessage("Deleted successfully"); + setSnackbarAlertOpen(true); + return; + }; + if (!assetId) { + onCompleted(); + return; + } + await deleteBanner({ + variables: { + orgAssetId: assetId, + }, + refetchQueries: [GET_ORG_CUSTOM_ASSETS], + onCompleted, + onError: () => { + showError("Error deleting image"); + }, + }); + }; + + return ( + <> + {}} + openSelectFile={() => {}} + imageType={AVATAR_EDITOR_TYPES.BANNER_IMAGE} + title={"Upload Image"} + {...selectedAvatarProps} + originalImage={tempImage || selectedAvatarProps?.originalImage} + onCancel={handleResetAvatarImage} + clearInput={() => setSelectedAvatarProps(selectedAvatarProps)} + /> + + + {commandBanners.map((banner) => { + return ( + + ); + })} + + + + ); +}; + +export default CustomizeBanners; diff --git a/wondrous-bot-admin/src/components/Settings/CustomizeBanners/styles.tsx b/wondrous-bot-admin/src/components/Settings/CustomizeBanners/styles.tsx new file mode 100644 index 0000000000..a73fdc35ae --- /dev/null +++ b/wondrous-bot-admin/src/components/Settings/CustomizeBanners/styles.tsx @@ -0,0 +1,205 @@ +import { Box, Divider, Grid, Typography } from "@mui/material"; +import styled from "styled-components"; + +export const CustomizeBannersContainer = styled((props) => )` + && { + display: flex; + flex: 1; + } +`; + +export const CommandsContainer = styled((props) => )` + && { + justify-content: center; + gap: 24px; + } + + ${({ theme }) => theme.breakpoints.up("xl")} { + && { + justify-content: flex-start; + } + } +`; + +export const CommandBannerContainer = styled(Grid)` + && { + background-color: white; + color: #4d4d4d; + border-radius: 16px; + width: 100%; + } + + ${({ theme }) => theme.breakpoints.up("sm")} { + max-width: 448px; + } +`; + +export const HeaderContainer = styled(Grid)` + && { + border-bottom: 1px solid #e0e0e0; + margin: 14px; + margin-bottom: 0; + } +`; + +export const HeaderContainerTooltipContent = styled(Grid)` + && { + display: flex; + gap: 8px; + width: fit-content; + } +`; + +export const HeaderText = styled(Typography)` + && { + padding-bottom: 8px; + font-size: 13px; + font-weight: 600; + } +`; + +export const CommandBannerUploadContainer = styled(Grid)` + ${({ theme }) => theme.breakpoints.up("lg")} { + display: flex; + } +`; + +export const BannerUploadContainer = styled(Grid)` + && { + padding: 14px; + flex: 1; + } +`; + +export const BannerUploadHeader = styled(Typography)` + && { + font-size: 13px; + font-weight: 600; + padding-bottom: 12px; + } +`; + +export const BannerUploadImageContainer = styled(Grid)` + && { + height: 70px; + width: 100%; + border-radius: 6px; + overflow: hidden; + } + + && > img { + height: 100%; + width: 100%; + object-fit: cover; + } +`; + +export const BannerUploadTextButtonContainer = styled(Grid)` + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding-top: 12px; + + ${({ theme }) => theme.breakpoints.up("md")} { + && { + flex-direction: column; + align-items: flex-start; + } + } +`; + +export const BannerUploadText = styled(Typography)` + && { + font-size: 11px; + font-weight: 500; + width: fit-content; + } +`; + +export const BannerUploadButtonContainer = styled(Grid)` + width: fit-content; + display: flex; + gap: 8px; +`; + +export const ButtonInputContainer = styled(Grid)` + && { + position: absolute; + + & > input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + opacity: 0; + } + } +`; + +export const SectionDivider = styled(Divider)` + ${({ theme }) => theme.breakpoints.up("md")} { + display: none; + } +`; + +export const TopImageSectionContainer = styled(Grid)` + && { + padding: 14px; + display: flex; + flex-direction: column; + } + + ${({ theme }) => theme.breakpoints.up("md")} { + && { + flex-direction: column; + } + } +`; + +export const TopImageImageButtonContainer = styled(Grid)` + display: flex; + gap: 12px; + + ${({ theme }) => theme.breakpoints.up("md")} { + && { + flex-direction: column; + } + } +`; + +export const TopImageContainer = styled(Grid)` + && { + height: 70px; + width: 70px; + border-radius: 1000px; + overflow: hidden; + } + && > img { + height: 100%; + width: 100%; + object-fit: cover; + } +`; + +export const TopImageTextButtonContainer = styled(Grid)` + && { + display: flex; + flex-direction: column; + gap: 12px; + } +`; + +export const TopImageText = styled(Typography)` + && { + font-weight: 500; + font-size: 11px; + } +`; + +export const TopImageButtonContainer = styled(Grid)` + display: flex; + gap: 8px; +`; diff --git a/wondrous-bot-admin/src/components/Settings/MenuSwitcher.tsx b/wondrous-bot-admin/src/components/Settings/MenuSwitcher.tsx index edbafa6ed4..a32edf5757 100644 --- a/wondrous-bot-admin/src/components/Settings/MenuSwitcher.tsx +++ b/wondrous-bot-admin/src/components/Settings/MenuSwitcher.tsx @@ -12,6 +12,10 @@ const MENU_ITEMS = [ title: "General Settings", path: "/settings", }, + { + title: "Customize Banners", + path: "/settings/customize-banners", + }, { title: "Notifications", path: "/settings/notifications", diff --git a/wondrous-bot-admin/src/components/Shared/Modal/index.tsx b/wondrous-bot-admin/src/components/Shared/Modal/index.tsx index 17035d2622..e48559bcb2 100644 --- a/wondrous-bot-admin/src/components/Shared/Modal/index.tsx +++ b/wondrous-bot-admin/src/components/Shared/Modal/index.tsx @@ -29,6 +29,7 @@ interface IModalProps { dialogComponentProps?: any; modalFooterStyle?: { padding?: string; + gap?: string; }; modalTitleStyle?: any; closeButtonStyle?: any; @@ -66,7 +67,13 @@ const Modal = ({ {!noHeader && ( {!!title && {title}} - {onClose && } + {onClose && ( + + )} )} diff --git a/wondrous-bot-admin/src/graphql/fragments/orgAsset.ts b/wondrous-bot-admin/src/graphql/fragments/orgAsset.ts new file mode 100644 index 0000000000..9f6a46a01f --- /dev/null +++ b/wondrous-bot-admin/src/graphql/fragments/orgAsset.ts @@ -0,0 +1,14 @@ +import { gql } from "@apollo/client"; + +export const OrgAssetFragment = gql` + fragment OrgAssetFragment on OrgAsset { + id + orgId + purpose + mediaType + slug + publicUrl + cdnUrl + removedAt + } +`; diff --git a/wondrous-bot-admin/src/graphql/mutations/orgAsset.ts b/wondrous-bot-admin/src/graphql/mutations/orgAsset.ts new file mode 100644 index 0000000000..964a900b4e --- /dev/null +++ b/wondrous-bot-admin/src/graphql/mutations/orgAsset.ts @@ -0,0 +1,18 @@ +import { gql } from "@apollo/client"; + +export const UPSERT_ORG_CUSTOM_BANNER = gql` + mutation upsertOrgCustomAsset($input: OrgAssetInput!) { + upsertOrgCustomAsset(input: $input) { + id + orgId + } + } +`; + +export const DELETE_ORG_BANNER = gql` + mutation deleteOrgCustomAsset($orgAssetId: ID!) { + deleteOrgCustomAsset(orgAssetId: $orgAssetId) { + success + } + } +`; diff --git a/wondrous-bot-admin/src/graphql/queries/orgAsset.ts b/wondrous-bot-admin/src/graphql/queries/orgAsset.ts new file mode 100644 index 0000000000..437c9c2c29 --- /dev/null +++ b/wondrous-bot-admin/src/graphql/queries/orgAsset.ts @@ -0,0 +1,11 @@ +import { gql } from "@apollo/client"; +import { OrgAssetFragment } from "graphql/fragments/orgAsset"; + +export const GET_ORG_CUSTOM_ASSETS = gql` + query getOrgCustomAssets($orgId: ID!) { + getOrgCustomAssets(orgId: $orgId) { + ...OrgAssetFragment + } + } + ${OrgAssetFragment} +`; diff --git a/wondrous-bot-admin/src/pages/settings/customize-banners.tsx b/wondrous-bot-admin/src/pages/settings/customize-banners.tsx new file mode 100644 index 0000000000..77a5a54402 --- /dev/null +++ b/wondrous-bot-admin/src/pages/settings/customize-banners.tsx @@ -0,0 +1,14 @@ +import CustomizeBanners from "components/Settings/CustomizeBanners"; +import SettingsLayout from "components/Shared/SettingsLayout"; + +const SettingsPage = () => { + return ( + <> + + + + + ); +}; + +export default SettingsPage; diff --git a/wondrous-bot-admin/src/types/assets.tsx b/wondrous-bot-admin/src/types/assets.tsx index ce0e33eda1..94b037f100 100644 --- a/wondrous-bot-admin/src/types/assets.tsx +++ b/wondrous-bot-admin/src/types/assets.tsx @@ -1 +1 @@ -export type AvatarEditorTypes = 'HEADER_IMAGE' | 'ICON_IMAGE'; +export type AvatarEditorTypes = "HEADER_IMAGE" | "ICON_IMAGE" | "BANNER_IMAGE" | "BANNER_LOGO_IMAGE";