- {image && (
-
- )}
+
+
+ {userData?.name ? userData.name.charAt(0).toUpperCase() : ""}
+
+
Hello {userData?.name ? userData.name.split(" ")[0] : ""}
diff --git a/components/SlidingGallery.tsx b/components/SlidingGallery.tsx
index 105683d..bf6bd31 100644
--- a/components/SlidingGallery.tsx
+++ b/components/SlidingGallery.tsx
@@ -3,115 +3,158 @@ import Link from "next/link";
import Image from "next/image";
import styles from "../css/SlidingGallery.module.css";
-const views = [
- {
- id: "1",
- content: (
-
-
-
-
Meet, Share, and Learn!
-
Join Facebook Community
-
-
-
-
-
-
- ),
- },
- {
- id: "2",
- content: (
-
-
-
-
Meet, Share, and Learn!
-
Join Facebook Community
-
-
-
-
-
-
- ),
- },
- {
- id: "3",
- content: (
-
-
-
-
Meet, Share, and Learn!
-
Join Facebook Community
-
-
-
-
-
-
- ),
- },
-];
-
-const SlidingGallery = () => {
+const SlidingGallery = ({
+ views = [],
+ className = "",
+ showPagination = true,
+ autoScroll = false,
+ autoScrollInterval = 5000,
+ onSlideChange = () => {},
+ height = "100vh",
+}) => {
const [activeIdx, setActiveIdx] = useState(0);
+ const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const scrollRef = useRef(null);
+ const galleryRef = useRef(null);
+ const autoScrollRef = useRef(null);
- const handleScroll = (event) => {
- const scrollLeft = event.target.scrollLeft;
- const slideWidth = event.target.clientWidth;
- const index = Math.round(scrollLeft / slideWidth);
- setActiveIdx(index);
+ // Auto-scroll functionality
+ useEffect(() => {
+ if (autoScroll && views.length > 1) {
+ autoScrollRef.current = setInterval(() => {
+ setActiveIdx((prevIdx) => {
+ const nextIdx = (prevIdx + 1) % views.length;
+ goToSlide(nextIdx);
+ return nextIdx;
+ });
+ }, autoScrollInterval);
+
+ return () => {
+ if (autoScrollRef.current) {
+ clearInterval(autoScrollRef.current);
+ }
+ };
+ }
+ }, [autoScroll, autoScrollInterval, views.length]);
+
+ // Clear auto-scroll on user interaction
+ const handleUserInteraction = () => {
+ if (autoScrollRef.current) {
+ clearInterval(autoScrollRef.current);
+ }
};
+ useEffect(() => {
+ const updateDimensions = () => {
+ if (galleryRef.current) {
+ setDimensions({
+ width: galleryRef.current.clientWidth,
+ height: galleryRef.current.clientHeight,
+ });
+ }
+ };
+
+ // Initial dimension update
+ updateDimensions();
+
+ // Add resize listener
+ window.addEventListener("resize", updateDimensions);
+
+ // Cleanup
+ return () => window.removeEventListener("resize", updateDimensions);
+ }, []);
+
+ useEffect(() => {
+ // Recalculate active index when dimensions change
+ if (scrollRef.current && dimensions.width > 0) {
+ const scrollLeft = scrollRef.current.scrollLeft;
+ const slideWidth = dimensions.width;
+ const index = Math.round(scrollLeft / slideWidth);
+ setActiveIdx(index);
+ }
+ }, [dimensions]);
+
+ const handleScroll = (event) => {
+ handleUserInteraction();
+ const scrollLeft = event.target.scrollLeft;
+ const slideWidth = dimensions.width;
+ const index = Math.round(scrollLeft / slideWidth);
+ if (index !== activeIdx) {
+ setActiveIdx(index);
+ onSlideChange(index);
+ }
+ };
+
+ const goToSlide = (index) => {
+ if (scrollRef.current) {
+ scrollRef.current.scrollTo({
+ left: index * dimensions.width,
+ behavior: "smooth",
+ });
+ }
+ };
+
+ const handleDotClick = (index) => {
+ handleUserInteraction();
+ goToSlide(index);
+ setActiveIdx(index);
+ onSlideChange(index);
+ };
+
+ // Early return if no views
+ if (!views || views.length === 0) {
+ return (
+
+
+
No content to display
+
+
+ );
+ }
+
return (
-
+
{views.map((item) => (
-
-
- {views.map((_, index) => (
-
- ))}
-
+ {showPagination && views.length > 1 && (
+
+ {views.map((_, index) => (
+
handleDotClick(index)}
+ style={{ cursor: "pointer" }}
+ />
+ ))}
+
+ )}
);
};
diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx
new file mode 100644
index 0000000..71e428b
--- /dev/null
+++ b/components/ui/avatar.tsx
@@ -0,0 +1,53 @@
+"use client"
+
+import * as React from "react"
+import * as AvatarPrimitive from "@radix-ui/react-avatar"
+
+import { cn } from "@/lib/utils"
+
+function Avatar({
+ className,
+ ...props
+}: React.ComponentProps
) {
+ return (
+
+ )
+}
+
+function AvatarImage({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx
new file mode 100644
index 0000000..0205413
--- /dev/null
+++ b/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : "span"
+
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/css/SlidingGallery.module.css b/css/SlidingGallery.module.css
index 43f024f..d60a608 100644
--- a/css/SlidingGallery.module.css
+++ b/css/SlidingGallery.module.css
@@ -1,120 +1,167 @@
+/* SlidingGallery.module.css */
+
.gallery {
- height: 200px;
- width: 100%;
- border: 1px solid #113768;
- border-radius: 25px;
- position: relative;
- overflow: hidden;
- }
-
- .scrollContainer {
- display: flex;
- height: 100%;
- overflow-x: auto;
- scroll-behavior: smooth;
- scroll-snap-type: x mandatory;
- scrollbar-width: none; /* Firefox */
- -ms-overflow-style: none; /* Internet Explorer 10+ */
- }
-
- .scrollContainer::-webkit-scrollbar {
- display: none; /* WebKit */
- }
-
- .slide {
- min-width: 100%;
- flex-shrink: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- scroll-snap-align: start;
- box-sizing: border-box;
- }
-
-
- .link {
- text-decoration: none;
- color: inherit;
- width: 100%;
+ position: relative;
+ width: 100%;
+ height: 100vh; /* Default height, can be overridden by props */
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+.emptyState {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #666;
+ font-size: 1.2rem;
+}
+
+.scrollContainer {
+ flex: 1;
+ display: flex;
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+}
+
+.scrollContainer::-webkit-scrollbar {
+ display: none; /* Chrome, Safari, Opera */
+}
+
+.slide {
+ min-width: 100%;
+ scroll-snap-align: start;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ box-sizing: border-box;
+}
+
+.link {
+ width: 100%;
+ height: 100%;
+ display: block;
+ text-decoration: none;
+ color: inherit;
+}
+
+.facebook {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 40px;
+ box-sizing: border-box;
+ background: linear-gradient(135deg, #1877f2 0%, #42a5f5 100%);
+ border-radius: 20px;
+ color: white;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
+ transition: transform 0.3s ease;
+}
+
+.facebook:hover {
+ transform: translateY(-5px);
+}
+
+.textView {
+ flex: 1;
+ padding-right: 20px;
+}
+
+.facebookOne {
+ font-size: clamp(1.5rem, 4vw, 2.5rem);
+ font-weight: 700;
+ margin: 0 0 16px 0;
+ line-height: 1.2;
+}
+
+.facebookTwo {
+ font-size: clamp(1rem, 2.5vw, 1.25rem);
+ margin: 0;
+ opacity: 0.9;
+ line-height: 1.4;
+}
+
+.logoView {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.logoView img {
+ width: clamp(120px, 15vw, 120px);
+ height: clamp(120px, 15vw, 120px);
+ object-fit: contain;
+}
+
+.pagination {
+ position: absolute;
+ bottom: 15px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 6px;
+ z-index: 10;
+}
+
+.dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ transition: all 0.3s ease;
+ cursor: pointer;
+}
+
+.activeDot {
+ background-color: #113768;
+}
+
+.inactiveDot {
+ background-color: #b1d3ff;
+}
+
+.inactiveDot:hover {
+ background-color: rgba(255, 255, 255, 0.8);
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .gallery {
+ height: 70vh; /* Adjust for mobile */
}
.facebook {
- display: flex;
- justify-content: space-evenly;
- flex-direction: row;
- height: 100%;
- background-color: #fff;
- border-radius: 25px;
- padding: 40px 40px;
- box-sizing: border-box;
- }
-
- .facebookOne {
- font-family: 'Montserrat', sans-serif;
- font-weight: 900;
- color: #113768;
- font-size: 20px;
- margin: 0;
- }
-
- .facebookTwo {
- font-family: 'Montserrat', sans-serif;
- font-weight: 600;
- color: #113768;
- font-size: 13px;
- margin: 0;
- }
-
- .pagination {
- display: flex;
- flex-direction: row;
- position: absolute;
- bottom: 20px;
- left: 50%;
- transform: translateX(-50%);
- }
-
- .dot {
- width: 10px;
- height: 10px;
- border-radius: 5px;
- margin: 0 5px;
- }
-
- .activeDot {
- background-color: #113768;
- }
-
- .inactiveDot {
- background-color: #ccc;
+ flex-direction: column;
+ text-align: center;
+ padding: 30px 20px;
}
.textView {
- width: 60%;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
+ padding-right: 0;
+ margin-bottom: 20px;
}
- .logoView {
- width: 40%;
- display: flex;
- justify-content: flex-end;
- align-items: flex-end;
+ .slide {
+ padding: 15px;
+ }
+}
+
+@media (max-width: 480px) {
+ .gallery {
+ height: 60vh;
}
- /* Responsive adjustments */
- @media (max-width: 768px) {
- .slide {
- min-width: calc(100% - 40px);
- padding: 0 20px;
- }
-
- .facebookOne {
- font-size: 18px;
- }
-
- .facebookTwo {
- font-size: 12px;
- }
- }
\ No newline at end of file
+ .facebook {
+ padding: 20px 15px;
+ }
+
+ .slide {
+ padding: 10px;
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 4efd5db..caabb55 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,8 @@
"lint": "next lint"
},
"dependencies": {
+ "@radix-ui/react-avatar": "^1.1.10",
+ "@radix-ui/react-slot": "^1.2.3",
"capacitor-secure-storage-plugin": "^0.11.0",
"clsx": "^2.1.1",
"lucide-react": "^0.523.0",
diff --git a/public/images/icons/accuracy.png b/public/images/icons/accuracy.png
new file mode 100644
index 0000000..4ac1956
Binary files /dev/null and b/public/images/icons/accuracy.png differ
diff --git a/public/images/icons/attempt.png b/public/images/icons/attempt.png
new file mode 100644
index 0000000..ad7906d
Binary files /dev/null and b/public/images/icons/attempt.png differ
diff --git a/public/images/icons/error.png b/public/images/icons/error.png
new file mode 100644
index 0000000..b4af7ea
Binary files /dev/null and b/public/images/icons/error.png differ