From 7314367e49578c8ec687fbfea61bc94479173e30 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 23 Jan 2025 19:40:36 +0300 Subject: [PATCH 01/32] Profile Settings --- .../profile/components/ApiTokens.tsx | 110 ++++++++++++++++++ .../profile/components/MyProfile.tsx | 70 +++++++++++ .../profile/components/PasswordEdit.tsx | 69 +++++++++++ .../profile/components/ProfileTabs.tsx | 31 +++++ .../app/(authenticated)/profile/page.tsx | 11 ++ 5 files changed, 291 insertions(+) create mode 100644 netmanager-app/app/(authenticated)/profile/components/ApiTokens.tsx create mode 100644 netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx create mode 100644 netmanager-app/app/(authenticated)/profile/components/PasswordEdit.tsx create mode 100644 netmanager-app/app/(authenticated)/profile/components/ProfileTabs.tsx create mode 100644 netmanager-app/app/(authenticated)/profile/page.tsx diff --git a/netmanager-app/app/(authenticated)/profile/components/ApiTokens.tsx b/netmanager-app/app/(authenticated)/profile/components/ApiTokens.tsx new file mode 100644 index 0000000000..799618cf08 --- /dev/null +++ b/netmanager-app/app/(authenticated)/profile/components/ApiTokens.tsx @@ -0,0 +1,110 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" + +interface Token { + id: string + name: string + ipAddress: string + status: "Active" | "Inactive" + created: string + token: string + expires: string +} + +export default function ApiTokens() { + const [tokens, setTokens] = useState([ + { + id: "1", + name: "Production API", + ipAddress: "192.168.1.1", + status: "Active", + created: "2023-01-01", + token: "abc123xyz789", + expires: "2024-01-01", + }, + { + id: "2", + name: "Development API", + ipAddress: "192.168.1.2", + status: "Active", + created: "2023-02-01", + token: "def456uvw890", + expires: "2024-02-01", + }, + ]) + + const [newTokenName, setNewTokenName] = useState("") + + const handleCreateToken = (e: React.FormEvent) => { + e.preventDefault() + // Here you would typically send a request to your backend to create a new token + const newToken: Token = { + id: String(tokens.length + 1), + name: newTokenName, + ipAddress: "192.168.1." + (tokens.length + 1), + status: "Active", + created: new Date().toISOString().split("T")[0], + token: Math.random().toString(36).substring(2, 15), + expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString().split("T")[0], + } + setTokens([...tokens, newToken]) + setNewTokenName("") + } + + return ( +
+

API Access Tokens

+

+ Clients are used to generate API tokens that can be used to authenticate with the API. Your secret API tokens + are listed below. Remember to keep them secure and never share them. +

+
+
+
+ + setNewTokenName(e.target.value)} + required + /> +
+ +
+
+ + + + Client name + IP Address + Client Status + Created + Token + Expires + + + + {tokens.map((token) => ( + + {token.name} + {token.ipAddress} + {token.status} + {token.created} + {token.token} + {token.expires} + + ))} + +
+
+ ) +} + diff --git a/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx b/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx new file mode 100644 index 0000000000..42789aadd1 --- /dev/null +++ b/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx @@ -0,0 +1,70 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" + +export default function MyProfile() { + const [profile, setProfile] = useState({ + name: "John Doe", + email: "john@example.com", + username: "johndoe", + }) + + const [isEditing, setIsEditing] = useState(false) + + const handleInputChange = (e: React.ChangeEvent) => { + setProfile({ ...profile, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + // Here you would typically send the updated profile to your backend + console.log("Updated profile:", profile) + setIsEditing(false) + } + + return ( +
+

My Profile

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ {isEditing ? ( + + ) : ( + + )} +
+
+ ) +} + diff --git a/netmanager-app/app/(authenticated)/profile/components/PasswordEdit.tsx b/netmanager-app/app/(authenticated)/profile/components/PasswordEdit.tsx new file mode 100644 index 0000000000..b17cbedfb7 --- /dev/null +++ b/netmanager-app/app/(authenticated)/profile/components/PasswordEdit.tsx @@ -0,0 +1,69 @@ +"use client" + +import { useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" + +export default function PasswordEdit() { + const [passwords, setPasswords] = useState({ + current: "", + new: "", + confirm: "", + }) + + const handleInputChange = (e: React.ChangeEvent) => { + setPasswords({ ...passwords, [e.target.name]: e.target.value }) + } + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + // Here you would typically send the new password to your backend + console.log("Password change request:", passwords) + // Reset form after submission + setPasswords({ current: "", new: "", confirm: "" }) + } + + return ( +
+

Change Password

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ ) +} + diff --git a/netmanager-app/app/(authenticated)/profile/components/ProfileTabs.tsx b/netmanager-app/app/(authenticated)/profile/components/ProfileTabs.tsx new file mode 100644 index 0000000000..e2ce879cda --- /dev/null +++ b/netmanager-app/app/(authenticated)/profile/components/ProfileTabs.tsx @@ -0,0 +1,31 @@ +"use client" + +import { useState } from "react" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import MyProfile from "./MyProfile" +import PasswordEdit from "./PasswordEdit" +import ApiTokens from "./ApiTokens" + +export default function ProfileTabs() { + const [activeTab, setActiveTab] = useState("profile") + + return ( + + + My Profile + Password Edit + API Access Tokens + + + + + + + + + + + + ) +} + diff --git a/netmanager-app/app/(authenticated)/profile/page.tsx b/netmanager-app/app/(authenticated)/profile/page.tsx new file mode 100644 index 0000000000..bef2140964 --- /dev/null +++ b/netmanager-app/app/(authenticated)/profile/page.tsx @@ -0,0 +1,11 @@ +import ProfileTabs from "./components/ProfileTabs" + +export default function ProfilePage() { + return ( +
+

User Profile

+ +
+ ) +} + From efeceff568bf9af6275e29899f7f144370ce8ee9 Mon Sep 17 00:00:00 2001 From: Daniel Ntege Date: Thu, 23 Jan 2025 22:09:38 +0300 Subject: [PATCH 02/32] User Details --- .../profile/components/MyProfile.tsx | 208 ++++++++++++++---- netmanager-app/components/ui/textarea.tsx | 22 ++ netmanager-app/package-lock.json | 23 ++ netmanager-app/package.json | 2 + netmanager-app/utils/countries.ts | 13 ++ netmanager-app/utils/timezones.ts | 9 + 6 files changed, 240 insertions(+), 37 deletions(-) create mode 100644 netmanager-app/components/ui/textarea.tsx create mode 100644 netmanager-app/utils/countries.ts create mode 100644 netmanager-app/utils/timezones.ts diff --git a/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx b/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx index 42789aadd1..9404d27be8 100644 --- a/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx +++ b/netmanager-app/app/(authenticated)/profile/components/MyProfile.tsx @@ -1,38 +1,137 @@ -"use client" +"use client"; -import { useState } from "react" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" +import { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Avatar, AvatarImage } from "@/components/ui/avatar"; +import { getCountries } from "@/utils/countries"; +import { getTimezones } from "@/utils/timezones"; +import { users } from "@/core/apis/users"; +import { useAppSelector } from "@/core/redux/hooks"; export default function MyProfile() { - const [profile, setProfile] = useState({ - name: "John Doe", - email: "john@example.com", - username: "johndoe", - }) + const currentuser = useAppSelector((state) => state.user.userDetails); + const [profile, setProfile] = useState(null); + const [isEditing, setIsEditing] = useState(false); + const [countries, setCountries] = useState<{ value: string; label: string }[]>([]); + const [timezones, setTimezones] = useState<{ value: string; label: string }[]>([]); - const [isEditing, setIsEditing] = useState(false) + useEffect(() => { + const fetchUserData = async () => { + if (currentuser) { + const response = await users.getUserDetails(currentuser._id); + const userData = response.users[0]; + setProfile({ + firstName: userData.firstName, + lastName: userData.lastName, + email: userData.email, + jobTitle: userData.jobTitle, + country: userData.country, + timezone: userData.timezone, + bio: userData.description, + profilePicture: userData.profilePicture, + }); + } + }; + fetchUserData(); + }, [currentuser]); - const handleInputChange = (e: React.ChangeEvent) => { - setProfile({ ...profile, [e.target.name]: e.target.value }) - } + useEffect(() => { + setCountries(getCountries()); + setTimezones(getTimezones()); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + setProfile({ ...profile, [e.target.name]: e.target.value }); + }; + + const handleSelectChange = (name: string) => (value: string) => { + setProfile({ ...profile, [name]: value }); + }; const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - // Here you would typically send the updated profile to your backend - console.log("Updated profile:", profile) - setIsEditing(false) + e.preventDefault(); + console.log("Updated profile:", profile); + setIsEditing(false); + }; + + const handleImageUpload = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + setProfile({ ...profile, profilePicture: reader.result as string }); + }; + reader.readAsDataURL(file); + } + }; + + if (!profile) { + return
Loading...
; } return (
-

My Profile

-
+

Personal Information

+

Update your photo and personal details.

+ + +
+ + {profile?.profilePicture ? ( + + ) : ( +
+ No Image +
+ )} +
+
+ + + +
+
+
- - + + +
+
+ +
@@ -40,31 +139,66 @@ export default function MyProfile() { id="email" name="email" type="email" - value={profile.email} + value={profile.email || ""} onChange={handleInputChange} - disabled={!isEditing} + disabled={isEditing} />
- +
+
+ + +
+
+ + +
+
+ +
+ +