diff --git a/package.json b/package.json index de17b7cb7..78985a100 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "react-email": "2.1.5", "react-hook-form": "^7.52.2", "react-paginate": "^8.2.0", + "react-toastify": "^10.0.5", "recharts": "^2.12.7", "sharp": "^0.33.4", "swiper": "^11.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7b81ef63..a058996d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,9 @@ importers: react-paginate: specifier: ^8.2.0 version: 8.2.0(react@18.3.1) + react-toastify: + specifier: ^10.0.5 + version: 10.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recharts: specifier: ^2.12.7 version: 2.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -4484,6 +4487,12 @@ packages: '@types/react': optional: true + react-toastify@10.0.5: + resolution: {integrity: sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -10020,6 +10029,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + react-toastify@10.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.0 diff --git a/public/images/productimage.png b/public/images/productimage.png new file mode 100644 index 000000000..55cef508e Binary files /dev/null and b/public/images/productimage.png differ diff --git a/public/images/user.png b/public/images/user.png new file mode 100644 index 000000000..6f728aaa5 Binary files /dev/null and b/public/images/user.png differ diff --git a/public/messages/en.json b/public/messages/en.json index 2e703f3c0..eef7daedd 100644 --- a/public/messages/en.json +++ b/public/messages/en.json @@ -46,7 +46,7 @@ "otpExpiresIn": "OTP expires in: {timeLeft}", "resendCode": "Didn't receive the code? resend", "dataProcessing": "We would process your data as set forth in our Terms of Use, Privacy Policy and Data Processing Agreement", - "alreadyHaveAccount": "Already Have An Account? Login" + "alreadyHaveAccount": "Already Have An Account?" }, "HomePage": { "title": "Hello world!" @@ -153,6 +153,7 @@ }, "noJobs": { "noJobsTitle": "No available Jobs at the moment", + "noJobsContent": "Sign up for our newsletter to get updates on job postings", "modalTitle": "Newsletter", "button": "Sign up for newsletter", diff --git a/src/app/(landing-routes)/blog/page.tsx b/src/app/(landing-routes)/blog/page.tsx index 3ec42db7f..646d72f53 100644 --- a/src/app/(landing-routes)/blog/page.tsx +++ b/src/app/(landing-routes)/blog/page.tsx @@ -1,14 +1,62 @@ "use client"; +import axios from "axios"; +import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { getApiUrl } from "~/actions/getApiUrl"; import CustomButton from "~/components/common/common-button/common-button"; import HeroSection from "~/components/extDynamicPages/blogCollection/BlogPageHero"; import BlogCard from "~/components/layouts/BlogCards"; +import { useToast } from "~/components/ui/use-toast"; import { blogPosts } from "./data/mock"; const BlogHome = () => { const router = useRouter(); + const { toast } = useToast(); + const { data: session } = useSession(); + const [, setBlogPost] = useState(""); + + useEffect(() => { + async function fetchBlog() { + try { + const apiUrl = await getApiUrl(); + const token = session?.access_token; + const response = await axios.get(`${apiUrl}/api/v1/blogs`, { + headers: { + Authorization: `Bearer: ${token}`, + }, + }); + const data = response.data; + setBlogPost(data); + toast({ + title: + Array.isArray(data.data) && data.data.length === 0 + ? "No Blog Post Available" + : "Blog Posts Retrieved", + description: + Array.isArray(data.data) && data.data.length === 0 + ? "Blog posts are empty" + : "Blog posts retrieved successfully", + variant: + Array.isArray(data.data) && data.data.length === 0 + ? "destructive" + : "default", + }); + } catch (error) { + toast({ + title: "Error occured", + description: + error instanceof Error + ? error.message + : "An unknown error occurred", + variant: "destructive", + }); + } + } + fetchBlog(); + }, [session?.access_token, toast]); return (
diff --git a/src/app/dashboard/(admin)/admin/(settings)/settings/data-and-privacy/page.tsx b/src/app/dashboard/(admin)/admin/(settings)/settings/data-and-privacy/page.tsx index dfd73041e..aa0e9bd79 100644 --- a/src/app/dashboard/(admin)/admin/(settings)/settings/data-and-privacy/page.tsx +++ b/src/app/dashboard/(admin)/admin/(settings)/settings/data-and-privacy/page.tsx @@ -2,6 +2,8 @@ import { useState } from "react"; +import CustomButton from "~/components/common/common-button/common-button"; + interface ToggleSwitchProperties { label: string; description: string; @@ -21,7 +23,7 @@ const ToggleSwitch: React.FC = ({ return (
- +
); }; diff --git a/src/app/dashboard/(admin)/admin/(settings)/settings/notification/page.tsx b/src/app/dashboard/(admin)/admin/(settings)/settings/notification/page.tsx index e1e1d263f..431eb04df 100644 --- a/src/app/dashboard/(admin)/admin/(settings)/settings/notification/page.tsx +++ b/src/app/dashboard/(admin)/admin/(settings)/settings/notification/page.tsx @@ -77,7 +77,7 @@ const NotificationPage = () => { }; return ( -
+
Notification @@ -178,7 +178,7 @@ const NotificationPage = () => { /> -
+
} diff --git a/src/app/dashboard/(admin)/admin/(settings)/settings/organization/members/page.tsx b/src/app/dashboard/(admin)/admin/(settings)/settings/organization/members/page.tsx index 9fa6ea504..85488d79b 100644 --- a/src/app/dashboard/(admin)/admin/(settings)/settings/organization/members/page.tsx +++ b/src/app/dashboard/(admin)/admin/(settings)/settings/organization/members/page.tsx @@ -8,7 +8,14 @@ import { useState } from "react"; import CustomButton from "~/components/common/common-button/common-button"; import { Input } from "~/components/common/input"; import InviteMemberModal from "~/components/common/modals/invite-member"; +import DeleteMember from "~/components/common/modals/invite-member/DeleteMembers"; import LoadingSpinner from "~/components/miscellaneous/loading-spinner"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "~/components/ui/dropdown-menu"; import { Select, SelectContent, @@ -71,8 +78,15 @@ const activeMembers: number = memberData.length; const Members = () => { const [isModalOpen, setIsModalOpen] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [exporting, setExporting] = useState(false); const [text, setText] = useState("Export CSV"); + const [isVisible, setIsVisible] = useState(false); + + const toggleVisibility = () => { + setIsVisible(!isVisible); + }; const { toast } = useToast(); const handleCopy = () => { @@ -87,10 +101,18 @@ const Members = () => { setIsModalOpen(true); }; + const handleDeleteOpen = () => { + setIsDeleteModalOpen(true); + }; + const handleModalClose = () => { setIsModalOpen(false); }; + const handleDeleteClose = () => { + setIsDeleteModalOpen(false); + }; + const exportMembers = async () => { setExporting(true); setText("Exporting..."); @@ -160,25 +182,28 @@ const Members = () => { {}} + onChange={toggleVisibility} + checked={isVisible} />
-
- - https://www.figma.com/design/7hCSTNzQOJLl9aww6wEEd1/Managing-Users----Team-Learn-AI?node-i - - - Copy link - -
+ {isVisible && ( +
+ + https://www.figma.com/design/7hCSTNzQOJLl9aww6wEEd1/Managing-Users----Team-Learn-AI?node-i + + + Copy link + +
+ )}

Manage members

@@ -246,7 +271,19 @@ const Members = () => {
- {}} className="flex-shrink-0" /> + + + {}} + className="flex-shrink-0" + /> + + + + Delete Member + + + ); })} @@ -276,6 +313,7 @@ const Members = () => { + ); }; diff --git a/src/app/dashboard/(admin)/admin/(settings)/settings/payment-information/page.tsx b/src/app/dashboard/(admin)/admin/(settings)/settings/payment-information/page.tsx index bf10a7cec..fa909b5d2 100644 --- a/src/app/dashboard/(admin)/admin/(settings)/settings/payment-information/page.tsx +++ b/src/app/dashboard/(admin)/admin/(settings)/settings/payment-information/page.tsx @@ -213,10 +213,6 @@ const PaymentInformation = () => { - -
-

Compare all features

-
); }; diff --git a/src/app/dashboard/(admin)/admin/(settings)/settings/subscription/page.tsx b/src/app/dashboard/(admin)/admin/(settings)/settings/subscription/page.tsx new file mode 100644 index 000000000..db4d38750 --- /dev/null +++ b/src/app/dashboard/(admin)/admin/(settings)/settings/subscription/page.tsx @@ -0,0 +1,5 @@ +const page = () => { + return
page
; +}; + +export default page; diff --git a/src/app/dashboard/(user-dashboard)/products/_components/new-product-modal.tsx b/src/app/dashboard/(user-dashboard)/products/_components/new-product-modal.tsx index 69f7b2c77..04064b833 100644 --- a/src/app/dashboard/(user-dashboard)/products/_components/new-product-modal.tsx +++ b/src/app/dashboard/(user-dashboard)/products/_components/new-product-modal.tsx @@ -1,4 +1,5 @@ // components/admin/NewProductModal.tsx + import { zodResolver } from "@hookform/resolvers/zod"; import { AnimatePresence, motion } from "framer-motion"; import { Loader, X } from "lucide-react"; diff --git a/src/app/dashboard/(user-dashboard)/products/_components/product-detail-modal.tsx b/src/app/dashboard/(user-dashboard)/products/_components/product-detail-modal.tsx index 271c00fce..a49fddded 100644 --- a/src/app/dashboard/(user-dashboard)/products/_components/product-detail-modal.tsx +++ b/src/app/dashboard/(user-dashboard)/products/_components/product-detail-modal.tsx @@ -1,3 +1,5 @@ +"use client"; + import { AnimatePresence, motion } from "framer-motion"; import { Loader2, X } from "lucide-react"; import { useSession } from "next-auth/react"; diff --git a/src/app/dashboard/(user-dashboard)/products/_components/productcadrcomponent.tsx b/src/app/dashboard/(user-dashboard)/products/_components/productcadrcomponent.tsx new file mode 100644 index 000000000..e77bc85ed --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/products/_components/productcadrcomponent.tsx @@ -0,0 +1,67 @@ +"use client"; + +import Image, { StaticImageData } from "next/image"; + +interface ProductCardProperties { + title: string; + price: string; + description: string; + inStock: boolean; + imageUrl: string | StaticImageData; +} + +const Delete = () => { + alert("Are you sure you want to delete"); +}; + +const Edit = () => { + alert("Proceed to edit"); +}; + +const ProductCard: React.FC = ({ + title, + price, + description, + inStock, + imageUrl, +}) => { + return ( +
+ {title} +
+
+

{title}

+

{price}

+
+

{description}

+ + {inStock ? "In stock" : "Out of stock"} + +
+ + +
+
+
+ ); +}; + +export default ProductCard; diff --git a/src/app/dashboard/(user-dashboard)/products/mainproductdetailpage/page.tsx b/src/app/dashboard/(user-dashboard)/products/mainproductdetailpage/page.tsx new file mode 100644 index 000000000..55ba4ce42 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/products/mainproductdetailpage/page.tsx @@ -0,0 +1,382 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { ChangeEvent, FormEvent, useState } from "react"; + +import user from "../../../../../../public/images/user.png"; + +const Discard = () => { + alert("Changes discard"); +}; + +const ProductDetail = () => { + const [textValue, setTextValue] = useState("Product 2"); + const [messageValue, setmessageValue] = useState( + "A fusion of ripe bananas, pure honey, and succulent raspberries with our bread. Crafted to perfection.", + ); + const [image, setImage] = useState(); + const [smallQuantity, setSmallQuantity] = useState(0); + const [standardQuantity, setStandardQuantity] = useState(24); + const [largeQuantity, setLargeQuantity] = useState(0); + const [smallPrice, setSmallPrice] = useState("$12.00"); + const [standardPrice, setStandardPrice] = useState("$29.00"); + const [largePrice, setLargePrice] = useState("$32.00"); + + const handleSubmit = (event: FormEvent) => { + event.preventDefault(); + + if ( + !textValue.trim() || + !messageValue.trim() || + !smallQuantity || + !standardQuantity || + !largeQuantity || + !smallPrice || + !standardPrice || + !largePrice || + !largePrice || + !image + ) { + alert( + "Please fill in all the necessary fields including your image before submitting.", + ); + return; + } + alert("Details updated"); + }; + + const handleSmallQty = (event: ChangeEvent) => { + setSmallQuantity(Number(event.target.value)); + }; + + const handleStandardQty = (event: ChangeEvent) => { + setStandardQuantity(Number(event.target.value)); + }; + + const handleLargeQty = (event: ChangeEvent) => { + setLargeQuantity(Number(event.target.value)); + }; + + //upload media section + const handleImageChange = (event: ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setImage(reader.result as string); + }; + reader.readAsDataURL(file); + } + }; + + //comment section + const [comments, setComments] = useState([ + { + name: "Adetunji Oluwatobi", + date: "02 Jan, 2020", + time: "Wed 02:30pm", + comment: "Living a balanced lifestyle is essential...", + }, + { + name: "Afolabi Oyewole", + date: "02 Jan, 2020", + time: "Wed 02:30pm", + comment: "Living a balanced lifestyle is essential...", + }, + ]); + const [newComment, setNewComment] = useState(""); + + const handleCommentChange = ( + event: React.ChangeEvent, + ) => { + setNewComment(event.target.value); + }; + + const handleCommentSubmit = () => { + if (newComment.trim()) { + const currentDate = new Date(); + const formattedDate = `${currentDate.getDate()} ${currentDate.toLocaleString("default", { month: "short" })}, ${currentDate.getFullYear()}`; + const formattedTime = `${currentDate.toLocaleString("default", { weekday: "short" })} ${currentDate.getHours()}:${currentDate.getMinutes() < 10 ? "0" : ""}${currentDate.getMinutes()}pm`; + + const commentToAdd = { + name: "New User", + date: formattedDate, + time: formattedTime, + comment: newComment, + }; + + setComments([...comments, commentToAdd]); + setNewComment(""); + } + }; + + return ( +
+
+
+
+ + Products + + + {">"} + + + Product Details + +
+
+
+
+ +

Product 2

+ ● In stock +
+
+
+ Discard +
+ +
+
+ +
+
+
+

Product Details

+

+ Make quick changes to your product. +

+
+ +
+ + setTextValue(event.target.value)} + placeholder="Product title" + className="w-full rounded-md border border-gray-300 p-2 focus:outline-none" + /> +
+
+ + +
+
+ +
+

Stock

+

Add and remove products

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SizeStockPrice
Small + + + setSmallPrice(event.target.value)} + /> +
+
+
Standard + + + + setStandardPrice(event.target.value) + } + /> +
+
+
Large + + + setLargePrice(event.target.value)} + /> +
+ + +
+
+ +
+
+

Media

+
+

+ Upload media for your product. +

+
+
+ {image ? ( + Uploaded + ) : ( +

Image here

+ )} +
+ +
+
+ +
+
+

Status

+

Availability

+
+ +
+
+

Archive

+

Archive a product.

+
+ +
+
+
+
+
+ +
+

Comments

+ {comments.map((comment, index) => ( +
+
+ User Avatar +
+

{comment.name}

+

{`${comment.date} ${comment.time}`}

+
+
+

{comment.comment}

+
+ ))} + +
+ +
+
+
+ ); +}; + +export default ProductDetail; diff --git a/src/app/dashboard/(user-dashboard)/products/productcard/page.tsx b/src/app/dashboard/(user-dashboard)/products/productcard/page.tsx new file mode 100644 index 000000000..baca39210 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/products/productcard/page.tsx @@ -0,0 +1,19 @@ +import productimage from "/public/images/productimage.png"; + +import ProductCard from "../_components/productcadrcomponent"; + +const Home: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default Home; diff --git a/src/components/common/modals/invite-member/DeleteMembers/index.tsx b/src/components/common/modals/invite-member/DeleteMembers/index.tsx new file mode 100644 index 000000000..eb5c7e6ec --- /dev/null +++ b/src/components/common/modals/invite-member/DeleteMembers/index.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { Button } from "~/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "~/components/ui/dialog"; + +interface ModalProperties { + show: boolean; + onClose: () => void; +} + +const DeleteMember: React.FC = ({ show, onClose }) => { + return ( + + + + + + {" "} +

+ Delete Member +

+
+
+

+ Are you sure you want to delete Chad Bosewick ? All data will be + permanently removed. This action cannot be undone. +

+ +
+
+ + + +
+
+
+
+
+
+
+ ); +}; + +export default DeleteMember; diff --git a/src/components/layouts/footer/index.tsx b/src/components/layouts/footer/index.tsx index 6e0c3b550..91c8a4b24 100644 --- a/src/components/layouts/footer/index.tsx +++ b/src/components/layouts/footer/index.tsx @@ -156,8 +156,6 @@ const Footer = () => { { route: "termsOfUse", link: "/terms-and-conditions" }, ]; - // - return (