From 274cd1e35df28d9efc049f02db881d13b2b266f1 Mon Sep 17 00:00:00 2001 From: Ashish Baravaliya Date: Thu, 14 Dec 2023 13:14:13 -0500 Subject: [PATCH 1/3] feat: themes page responsive --- src/components/choose-avatar-dialog.tsx | 19 ++++++- src/components/export-theme-dialog.tsx | 2 +- src/components/filter-tags.tsx | 11 ++-- src/components/footer.tsx | 2 +- src/components/header-searchbar.tsx | 72 +++++++++++++++++++++++++ src/components/header.tsx | 64 ++-------------------- src/components/layout.tsx | 26 +++++++-- src/components/reset-password.tsx | 4 -- src/components/sidebar.tsx | 54 +++++++++++-------- src/components/verification-dialog.tsx | 4 +- src/constants/website.tsx | 58 ++++++++++---------- src/pages/index.tsx | 12 ----- src/pages/settings.tsx | 4 +- src/pages/themes/index.tsx | 3 +- 14 files changed, 191 insertions(+), 144 deletions(-) create mode 100644 src/components/header-searchbar.tsx diff --git a/src/components/choose-avatar-dialog.tsx b/src/components/choose-avatar-dialog.tsx index a43a61a..8a653cb 100644 --- a/src/components/choose-avatar-dialog.tsx +++ b/src/components/choose-avatar-dialog.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useToast } from "@/hooks/useToast"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog"; import { ReloadIcon } from "@radix-ui/react-icons"; @@ -19,6 +19,7 @@ export const ChooseAvatarDialog: React.FC = ({ const { addToast } = useToast(); const { data: session, update } = useSession(); const [loading, setLoading] = useState(false); + const [isMobileView, setIsMobileView] = useState(false); const [generate, setGenerate] = useState(0); const [selectedAvatar, setSelectedAvatar] = useState<{ config: AvatarFullConfig | null; @@ -53,6 +54,20 @@ export const ChooseAvatarDialog: React.FC = ({ } }; + useEffect(() => { + const handleResize = () => { + setIsMobileView(window.innerWidth < 768); + }; + + window.addEventListener("resize", handleResize); + + handleResize(); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + return ( = ({
{new Array(5).fill(1).map((_, i) => (
- {new Array(5).fill(1).map((_, j) => ( + {new Array(isMobileView ? 3 : 5).fill(1).map((_, j) => (
{ diff --git a/src/components/export-theme-dialog.tsx b/src/components/export-theme-dialog.tsx index 65595db..061e0e2 100644 --- a/src/components/export-theme-dialog.tsx +++ b/src/components/export-theme-dialog.tsx @@ -229,7 +229,7 @@ export const ExportThemeDialog: React.FC = ({ handleDraw(ctx, 4, true); const link = document.createElement("a"); - link.download = `ThemeGPT_Theme_${+new Date()}.png`; + link.download = `ThemeAI_Theme_${+new Date()}.png`; link.href = tempCanvas.toDataURL("image/png"); link.click(); }; diff --git a/src/components/filter-tags.tsx b/src/components/filter-tags.tsx index f964e3e..03110b8 100644 --- a/src/components/filter-tags.tsx +++ b/src/components/filter-tags.tsx @@ -27,12 +27,17 @@ export const FiterTags: React.FC = ({
- Filter by tags{selected.length ? ` [${selected.length}]` : ""} - +

+ Filter by tags{selected.length ? ` [${selected.length}]` : ""} +

+

+ Filter{selected.length ? ` [${selected.length}]` : ""} +

+
= ({ className }) => {
- Copyright © 2024 ThemeGPT. All rights reserved. + Copyright © 2024 ThemeAI. All rights reserved.
diff --git a/src/components/header-searchbar.tsx b/src/components/header-searchbar.tsx new file mode 100644 index 0000000..bc31c57 --- /dev/null +++ b/src/components/header-searchbar.tsx @@ -0,0 +1,72 @@ +import React, { useEffect } from "react"; +import { useHelpers } from "@/hooks/useHelpers"; +import { SeachBar } from "./ui/input"; +import { FiterTags } from "./filter-tags"; +import { useQuery } from "@tanstack/react-query"; +import { getTags } from "@/services/theme"; +import { Switch } from "./ui/switch"; +import { Label } from "./ui/label"; +import { INPUT_LIMIT } from "@/constants/website"; + +const HeaderSearchBar = () => { + const { + setThemeSearchQuery, + filterTags, + setFilterTags, + isAIOnly, + setIsAIOnly, + } = useHelpers(); + + const { data: tags } = useQuery(["tags"], getTags); + + useEffect(() => { + if (filterTags?.length) { + setThemeSearchQuery(""); + } + }, [filterTags]); + + return ( + <> + { + setThemeSearchQuery(""); + setFilterTags([]); + }} + onSearch={(string: string) => { + setThemeSearchQuery(string); + setFilterTags([]); + }} + /> + +
+ setIsAIOnly((isAIOnly) => !isAIOnly)} + /> + +
+ + ); +}; + +export default HeaderSearchBar; diff --git a/src/components/header.tsx b/src/components/header.tsx index 08d1202..25d72da 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -13,33 +13,20 @@ import { ResetPasswordDialog } from "./reset-password"; import { NewPasswordDialog } from "./new-password"; import { GenerateThemeDialog } from "./generate-theme-dialog"; import { useHelpers } from "@/hooks/useHelpers"; -import { useToast } from "@/hooks/useToast"; -import { SeachBar } from "./ui/input"; -import { FiterTags } from "./filter-tags"; -import { useQuery } from "@tanstack/react-query"; -import { getTags } from "@/services/theme"; import { HeaderMenu } from "./header-menu"; import MagicWand from "@/assets/svgs/magic-wand"; -import { Switch } from "./ui/switch"; -import { Label } from "./ui/label"; -import { INPUT_LIMIT } from "@/constants/website"; import { cn } from "@/lib/utils"; +import HeaderSearchBar from "./header-searchbar"; const Header = () => { const router = useRouter(); - const { addToast } = useToast(); const { loginOpen, setLoginOpen, - setThemeSearchQuery, - filterTags, - setFilterTags, verifyDialogState, setVerifyDialogState, generateThemeDialog, setGenerateThemeDialog, - isAIOnly, - setIsAIOnly, } = useHelpers(); const { status, data: session } = useSession(); const [singupOpen, setSingupOpen] = useState(false); @@ -47,8 +34,6 @@ const Header = () => { const [resetPasswordDialog, setResetPasswordDialog] = useState(false); const [newPasswordDialog, setNewPasswordDialog] = useState(false); - const { data: tags } = useQuery(["tags"], getTags); - const isAuthenticated = status === "authenticated"; const handleSignOut = async (path: string) => { @@ -122,12 +107,6 @@ const Header = () => { } }, [status]); - useEffect(() => { - if (filterTags?.length) { - setThemeSearchQuery(""); - } - }, [filterTags]); - const isLandingPage = router.pathname === "/"; return ( @@ -146,45 +125,8 @@ const Header = () => { {router.pathname === "/themes" ? ( -
- { - setThemeSearchQuery(""); - setFilterTags([]); - }} - onSearch={(string: string) => { - setThemeSearchQuery(string); - setFilterTags([]); - }} - /> - -
- setIsAIOnly((isAIOnly) => !isAIOnly)} - /> - -
+
+
) : null}
diff --git a/src/components/layout.tsx b/src/components/layout.tsx index fe73226..b2f912a 100644 --- a/src/components/layout.tsx +++ b/src/components/layout.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import Header from "./header"; import { useSession } from "next-auth/react"; import Sidebar from "./sidebar"; @@ -14,6 +14,7 @@ const Layout = ({ }) => { const session = useSession(); const router = useRouter(); + const [isMobileView, setIsMobileView] = useState(false); const width = router.pathname === "/themes/create" ? "250px" : "200px"; @@ -25,18 +26,35 @@ const Layout = ({ const isLandingPage = router.pathname === "/"; + useEffect(() => { + const handleResize = () => { + setIsMobileView(window.innerWidth < 768); + }; + + window.addEventListener("resize", handleResize); + + handleResize(); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + return staticLoading ? null : (
- {sidebar ? : null} + {sidebar ? : null} {sidebar ? ( -
+
{children}
) : ( diff --git a/src/components/reset-password.tsx b/src/components/reset-password.tsx index 63997e9..5edcda8 100644 --- a/src/components/reset-password.tsx +++ b/src/components/reset-password.tsx @@ -11,7 +11,6 @@ import { import { ReloadIcon } from "@radix-ui/react-icons"; import { Button } from "./ui/button"; import { sendPasswordResetEmail } from "@/services/user"; -import { useRouter } from "next/router"; import { INPUT_LIMIT } from "@/constants/website"; import { validateInput } from "@/lib/error"; @@ -24,7 +23,6 @@ export const ResetPasswordDialog: React.FC = ({ open, setOpen, }) => { - const router = useRouter(); const { addToast } = useToast(); const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(""); @@ -45,7 +43,6 @@ export const ResetPasswordDialog: React.FC = ({ title: "Password reset email has been sent", type: "success", }); - router.push("/themes", undefined, { shallow: true }); setOpen(false); }) .catch((error) => { @@ -62,7 +59,6 @@ export const ResetPasswordDialog: React.FC = ({ open={open} onOpenChange={() => { setOpen(false); - router.push("/themes", undefined, { shallow: true }); }} > diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index 8874cb3..1efc3b4 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -4,8 +4,15 @@ import { BarChart4, PartyPopper, ZapIcon } from "lucide-react"; import { useRouter } from "next/router"; import React from "react"; import { TemplateList } from "./template-list"; +import HeaderSearchBar from "./header-searchbar"; -const Sidebar = ({ width }: { width: string }) => { +const Sidebar = ({ + width, + isMobileView, +}: { + width: string; + isMobileView: boolean; +}) => { const router = useRouter(); const { themeType, setThemeType } = useHelpers(); @@ -31,30 +38,35 @@ const Sidebar = ({ width }: { width: string }) => { return (
{isTemplate ? ( ) : ( -
- {tabs.map((tab) => ( -
- setThemeType(tab.id as "explore" | "foryou" | "popular") - } - className={cn( - "flex gap-2.5 items-center py-2 px-8 mx-3 text-lg cursor-pointer hover:bg-primary/20", - themeType === tab.id && - "bg-primary/60 shadow-md hover:bg-primary/60" - )} - > - {tab.icon} - {tab.label} -
- ))} -
+ <> +
+ +
+
+ {tabs.map((tab) => ( +
+ setThemeType(tab.id as "explore" | "foryou" | "popular") + } + className={cn( + "flex gap-2.5 items-center py-2 px-2 md:px-8 mx-3 text-lg cursor-pointer hover:bg-primary/20", + themeType === tab.id && + "bg-primary/60 shadow-md hover:bg-primary/60" + )} + > + {tab.icon} + {tab.label} +
+ ))} +
+ )}
); diff --git a/src/components/verification-dialog.tsx b/src/components/verification-dialog.tsx index b577665..e55e596 100644 --- a/src/components/verification-dialog.tsx +++ b/src/components/verification-dialog.tsx @@ -145,14 +145,14 @@ export const VerificationDialog: React.FC = ({ clearURL && router.push("/themes", undefined, { shallow: true }); }} > - +
{messages[type].icon}
- + {messages[type].title} diff --git a/src/constants/website.tsx b/src/constants/website.tsx index ce44074..fd4f07d 100644 --- a/src/constants/website.tsx +++ b/src/constants/website.tsx @@ -8,9 +8,9 @@ export interface PrivacyPolicyProps { export const privacyPolicy: PrivacyPolicyProps[] = [ { - title: "Privacy Policy for ThemeGPT", + title: "Privacy Policy for ThemeAI", description: - "Welcome to ThemeGPT. At ThemeGPT, we are committed to safeguarding your privacy and ensuring the security of your personal information. This Privacy Policy outlines how we collect, use, and protect your data while using our website and services. By using ThemeGPT, you agree to the practices described in this policy.", + "Welcome to ThemeAI. At ThemeAI, we are committed to safeguarding your privacy and ensuring the security of your personal information. This Privacy Policy outlines how we collect, use, and protect your data while using our website and services. By using ThemeAI, you agree to the practices described in this policy.", }, { title: "Information We Collect", @@ -34,7 +34,7 @@ export const privacyPolicy: PrivacyPolicyProps[] = [ Social Interaction Data: - When interacting with other users on ThemeGPT, such as liking and saving + When interacting with other users on ThemeAI, such as liking and saving themes or following other users, we may collect and store these interactions.
@@ -50,7 +50,7 @@ export const privacyPolicy: PrivacyPolicyProps[] = [ Experience Points:
We collect and store information related to experience points and levels - you achieve on ThemeGPT. + you achieve on ThemeAI.
), }, @@ -113,7 +113,7 @@ export const privacyPolicy: PrivacyPolicyProps[] = [ { title: "Changes to Privacy Policy", description: - "This Privacy Policy may be updated from time to time. We will notify you of any significant changes. Continued use of ThemeGPT after these changes implies your acceptance of the updated policy.", + "This Privacy Policy may be updated from time to time. We will notify you of any significant changes. Continued use of ThemeAI after these changes implies your acceptance of the updated policy.", }, { title: "Contact Information", @@ -123,12 +123,12 @@ export const privacyPolicy: PrivacyPolicyProps[] = [ "Please contact us with any questions or comments about this Privacy Policy, Our Privacy Policy Toward Children, your personal information, and our third-party disclosure practices, at " } - contact@themegpt.co + contact@aithemes.io .

{ - "Thank you for using ThemeGPT. We appreciate your trust in us and are committed to ensuring your privacy and data protection." + "Thank you for using ThemeAI. We appreciate your trust in us and are committed to ensuring your privacy and data protection." }
Last updated: {moment("2023-10-31").format("MMM D, YYYY")} @@ -140,14 +140,14 @@ export const privacyPolicy: PrivacyPolicyProps[] = [ export const TermsOfUse = [ { - title: "Terms of Use for ThemeGPT", + title: "Terms of Use for ThemeAI", description: - "Welcome to ThemeGPT, your destination for creating captivating color themes, utilizing AI-driven prompts, and connecting with a creative community. By using ThemeGPT, you agree to abide by the following Terms of Use. Please read these terms carefully to ensure a positive experience on our platform.", + "Welcome to ThemeAI, your destination for creating captivating color themes, utilizing AI-driven prompts, and connecting with a creative community. By using ThemeAI, you agree to abide by the following Terms of Use. Please read these terms carefully to ensure a positive experience on our platform.", }, { title: "Acceptance of Terms", description: - "By accessing or using ThemeGPT, you acknowledge and agree to these Terms of Use. If you do not agree to these terms, please refrain from using our platform.", + "By accessing or using ThemeAI, you acknowledge and agree to these Terms of Use. If you do not agree to these terms, please refrain from using our platform.", }, { title: "Account Registration", @@ -156,7 +156,7 @@ export const TermsOfUse = [ User Accounts: - To access certain features of ThemeGPT, you may be required to create an + To access certain features of ThemeAI, you may be required to create an account. You are responsible for maintaining the confidentiality of your account information and any activities that occur under your account.
@@ -177,14 +177,14 @@ export const TermsOfUse = [ Compliance with Laws: - You agree to use ThemeGPT in compliance with all applicable local, - state, national, and international laws and regulations. + You agree to use ThemeAI in compliance with all applicable local, state, + national, and international laws and regulations.

Prohibited Activities: - While using ThemeGPT, you agree not to engage in any unlawful, abusive, + While using ThemeAI, you agree not to engage in any unlawful, abusive, or harmful activities, including but not limited to:
  • Harassment or discrimination against other users.
  • @@ -199,7 +199,7 @@ export const TermsOfUse = [ Community Guidelines: - ThemeGPT has specific community guidelines, which must be followed to + ThemeAI has specific community guidelines, which must be followed to ensure a positive experience for all users. Violation of these guidelines may result in account suspension or termination.
@@ -212,20 +212,20 @@ export const TermsOfUse = [ User-Generated Content: - When using ThemeGPT, you retain ownership of your user-generated - content, including color themes and other contributions. However, by - submitting content, you grant ThemeGPT a non-exclusive, royalty-free - license to use, reproduce, and distribute your content for the purpose - of providing our services. + When using ThemeAI, you retain ownership of your user-generated content, + including color themes and other contributions. However, by submitting + content, you grant ThemeAI a non-exclusive, royalty-free license to use, + reproduce, and distribute your content for the purpose of providing our + services.

Intellectual Property: - ThemeGPT and its associated logos, software, and other intellectual + ThemeAI and its associated logos, software, and other intellectual property are protected by copyright, trademark, and other intellectual - property laws. You may not copy, modify, or distribute any part of - ThemeGPT without our explicit consent. + property laws. You may not copy, modify, or distribute any part of AI + Themes without our explicit consent.
), }, @@ -244,20 +244,20 @@ export const TermsOfUse = [ Premium Features: - ThemeGPT may offer premium features or services that are subject to - their own terms and conditions. + ThemeAI may offer premium features or services that are subject to their + own terms and conditions.
), }, { title: "Termination of Services", description: - "We reserve the right to terminate, suspend, or limit access to ThemeGPT without notice, for any reason, including violation of these Terms of Use.", + "We reserve the right to terminate, suspend, or limit access to ThemeAI without notice, for any reason, including violation of these Terms of Use.", }, { title: "Changes to Terms", description: - "These Terms of Use may be updated periodically. We will notify you of any significant changes. Your continued use of ThemeGPT following these updates indicates your acceptance of the revised terms.", + "These Terms of Use may be updated periodically. We will notify you of any significant changes. Your continued use of ThemeAI following these updates indicates your acceptance of the revised terms.", }, { title: "Contact Information", @@ -267,12 +267,12 @@ export const TermsOfUse = [ "Please contact us with any questions or comments about this Privacy Policy, Our Privacy Policy Toward Children, your personal information, and our third-party disclosure practices, at " } - contact@themegpt.co + contact@ThemeAI.co .

{ - "Thank you for using ThemeGPT. We appreciate your trust in us and are committed to ensuring your privacy and data protection." + "Thank you for using ThemeAI. We appreciate your trust in us and are committed to ensuring your privacy and data protection." }
Last updated: {moment("2023-10-31").format("MMM D, YYYY")} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4a348b8..81fa13f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,4 +1,3 @@ -import CanvasAnimation from "@/components/canvas-animation"; import { Footer } from "@/components/footer"; import GlowingBoxes from "@/components/growing-dots"; import { Button } from "@/components/ui/button"; @@ -55,17 +54,6 @@ export default function Home() {
-
- -
-

- ThemeGPT -

-
-