From f75751a949c3ea68ada75be77df9e627bb80c82e Mon Sep 17 00:00:00 2001 From: Kartavya Bang Date: Sun, 9 Mar 2025 23:16:42 +0530 Subject: [PATCH 1/3] Added : Basic admin portal structure --- package.json | 3 + src/components/Admin.tsx | 150 +++++++ src/components/LoginForm.tsx | 2 - src/components/admin/ActivitiesEditor.tsx | 362 +++++++++++++++++ src/components/admin/EventsEditor.tsx | 462 ++++++++++++++++++++++ src/components/admin/HeroEditor.tsx | 134 +++++++ src/components/admin/MarkdownEditor.tsx | 63 +++ src/components/admin/ProjectsEditor.tsx | 251 ++++++++++++ src/components/admin/TeamEditor.tsx | 364 +++++++++++++++++ src/pages/admin/page.tsx | 9 +- yarn.lock | 191 +++++++++ 11 files changed, 1982 insertions(+), 9 deletions(-) create mode 100644 src/components/Admin.tsx create mode 100644 src/components/admin/ActivitiesEditor.tsx create mode 100644 src/components/admin/EventsEditor.tsx create mode 100644 src/components/admin/HeroEditor.tsx create mode 100644 src/components/admin/MarkdownEditor.tsx create mode 100644 src/components/admin/ProjectsEditor.tsx create mode 100644 src/components/admin/TeamEditor.tsx diff --git a/package.json b/package.json index bde31bc..0b1860f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "easymde": "^2.20.0", "embla-carousel-react": "^8.3.0", "framer-motion": "^12.4.7", "input-otp": "^1.2.4", @@ -59,9 +60,11 @@ "react-hook-form": "^7.54.2", "react-markdown": "^10.1.0", "react-resizable-panels": "^2.1.3", + "react-simplemde-editor": "^5.2.0", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.12.7", "remark-gfm": "^4.0.1", + "simplemde": "^1.11.2", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", diff --git a/src/components/Admin.tsx b/src/components/Admin.tsx new file mode 100644 index 0000000..d3f7bad --- /dev/null +++ b/src/components/Admin.tsx @@ -0,0 +1,150 @@ +import { useState } from "react"; +import { motion } from "framer-motion"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { useToast } from "@/hooks/use-toast"; +import Link from "next/link"; +import { Home, FileText, Calendar, Users, ExternalLink } from "lucide-react"; +import HeroEditor from "@/components/admin/HeroEditor"; +import ProjectsEditor from "@/components/admin/ProjectsEditor"; +import ActivitiesEditor from "@/components/admin/ActivitiesEditor"; +import EventsEditor from "@/components/admin/EventsEditor"; +import TeamEditor from "@/components/admin/TeamEditor"; + +const Admin = () => { + const { toast } = useToast(); + const [activeTab, setActiveTab] = useState("hero"); + + return ( +
+ +
+
+

Admin Dashboard

+

Manage website content

+
+ +
+ + +
+ + + Hero Section + + + Projects + + + Activities + + + Events + + + Team + + +
+ + + + + {activeTab === "hero" && "Hero Section Editor"} + {activeTab === "projects" && "Projects Editor"} + {activeTab === "activities" && "Activities Editor"} + {activeTab === "events" && "Events Editor"} + {activeTab === "team" && "Team Members Editor"} + + + {activeTab === "hero" && "Edit the main heading, text and background images"} + {activeTab === "projects" && "Add, edit or remove projects"} + {activeTab === "activities" && "Manage robotics activities and workshops"} + {activeTab === "events" && "Add or edit upcoming events and competitions"} + {activeTab === "team" && "Manage team members and their information"} + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+

Preview Pages

+
+ + +

Activities Page

+

View all robotics activities and workshops.

+ +
+
+ + + +

Events Page

+

View upcoming events and competitions.

+ +
+
+ + + +

Team Page

+

View all team members and their profiles.

+ +
+
+
+
+
+
+ ); +}; + +export default Admin; diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 335776c..c0b6414 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -49,8 +49,6 @@ const LoginForm = () => { } if (data) { - document.cookie = `sb-access-token=${data.session.access_token}; path=/; Secure`; - document.cookie = `sb-refresh-token=${data.session.refresh_token}; path=/; Secure`; router.push("/admin/page"); } diff --git a/src/components/admin/ActivitiesEditor.tsx b/src/components/admin/ActivitiesEditor.tsx new file mode 100644 index 0000000..5ff32d9 --- /dev/null +++ b/src/components/admin/ActivitiesEditor.tsx @@ -0,0 +1,362 @@ +import { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Label } from "@/components/ui/label"; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"; +import { useToast } from "@/hooks/use-toast"; +import { Plus, Trash, Edit, Save } from "lucide-react"; +import { Card } from "@/components/ui/card"; + +// Default activity data structure +const defaultActivities = [ + { + id: "1", + title: "Robotics Workshop", + shortDescription: "Learn robotics basics in a hands-on environment", + description: "A comprehensive workshop covering the fundamentals of robotics including mechanics, electronics, and programming.", + duration: "4 hours", + frequency: "Monthly", + participants: "20-30 students", + tags: ["Workshop", "Beginners", "Hands-on"] + } +]; + +const ActivitiesEditor = () => { + const { toast } = useToast(); + const [activities, setActivities] = useState(defaultActivities); + const [newActivity, setNewActivity] = useState({ + id: "", + title: "", + shortDescription: "", + description: "", + duration: "", + frequency: "", + participants: "", + tags: [] + }); + const [editingId, setEditingId] = useState(null); + const [tagInput, setTagInput] = useState(""); + + // Load activities from localStorage if exists + useEffect(() => { + const savedActivities = localStorage.getItem("activitiesData"); + if (savedActivities) { + try { + setActivities(JSON.parse(savedActivities)); + } catch (error) { + console.error("Failed to parse saved activities data:", error); + } + } + }, []); + + const handleSaveAll = () => { + localStorage.setItem("activitiesData", JSON.stringify(activities)); + toast({ + title: "Changes saved", + description: "Activities have been updated successfully.", + }); + }; + + const handleAddActivity = () => { + if (!newActivity.title || !newActivity.shortDescription) { + toast({ + title: "Error", + description: "Title and short description are required", + variant: "destructive", + }); + return; + } + + const newId = Date.now().toString(); + setActivities(prev => [...prev, { ...newActivity, id: newId }]); + setNewActivity({ + id: "", + title: "", + shortDescription: "", + description: "", + duration: "", + frequency: "", + participants: "", + tags: [] + }); + }; + + const handleUpdateActivity = () => { + if (!editingId) return; + + setActivities(prev => + prev.map(activity => + activity.id === editingId ? newActivity : activity + ) + ); + setEditingId(null); + setNewActivity({ + id: "", + title: "", + shortDescription: "", + description: "", + duration: "", + frequency: "", + participants: "", + tags: [] + }); + }; + + const handleEditActivity = (activity) => { + setNewActivity(activity); + setEditingId(activity.id); + }; + + const handleRemoveActivity = (id) => { + setActivities(prev => prev.filter(activity => activity.id !== id)); + if (editingId === id) { + setEditingId(null); + setNewActivity({ + id: "", + title: "", + shortDescription: "", + description: "", + duration: "", + frequency: "", + participants: "", + tags: [] + }); + } + }; + + const handleAddTag = () => { + if (tagInput.trim()) { + setNewActivity(prev => ({ + ...prev, + tags: [...(prev.tags || []), tagInput.trim()] + })); + setTagInput(""); + } + }; + + const handleRemoveTag = (index) => { + setNewActivity(prev => ({ + ...prev, + tags: prev.tags.filter((_, i) => i !== index) + })); + }; + + return ( +
+ +

+ {editingId ? "Edit Activity" : "Add New Activity"} +

+
+
+ + setNewActivity(prev => ({ ...prev, title: e.target.value }))} + placeholder="Activity title" + /> +
+ +
+ + setNewActivity(prev => ({ ...prev, shortDescription: e.target.value }))} + placeholder="Brief activity description" + /> +
+ +
+ +