Skip to content

Commit

Permalink
Merge pull request #168 from priyanshuverma-dev/mg-profile-and-settings
Browse files Browse the repository at this point in the history
migration: profile page and updating profile
  • Loading branch information
kom-senapati authored Oct 22, 2024
2 parents 230cbbc + 172e164 commit b99183f
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 21 deletions.
39 changes: 33 additions & 6 deletions app/api_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,14 @@ def api_publish_chatbot(chatbot_id: int) -> Union[Response, tuple[Response, int]


@api_bp.route("/api/profile/edit", methods=["POST"])
@login_required
@jwt_required()
def api_profile_edit() -> Union[Response, tuple[Response, int]]:
"""API endpoint to edit the user's profile."""
user: User = User.query.get_or_404(current_user.uid)

username: str = request.form["username"]
name: str = request.form["name"]
bio: str = request.form["bio"]
user = get_current_user()
data = request.get_json()
username: str = data.get("username")
name: str = data.get("name")
bio: str = data.get("bio")

user.name = name
user.username = username
Expand Down Expand Up @@ -643,3 +643,30 @@ def api_get_hub_data():

except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500


@api_bp.route("/api/user/<string:username>", methods=["GET"])
@jwt_required()
def api_get_user_data(username: str):
try:
user: Optional[User] = User.query.filter_by(username=username).first()
if user == None:
return jsonify({"success": False, "message": "User not found"}), 404

public_chatbots: List[Chatbot] = Chatbot.query.filter(
Chatbot.user_id == user.uid, Chatbot.public == True
).all()
public_images: List[Image] = Image.query.filter(
Image.user_id == user.uid, Image.public == True
).all()
# Response data
response = {
"success": True,
"user": user.to_dict(),
"bots": [bot.to_dict() for bot in public_chatbots],
"images": [image.to_dict() for image in public_images],
}
return jsonify(response), 200

except Exception as e:
return jsonify({"success": False, "message": str(e)}), 500
10 changes: 10 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ def __repr__(self) -> str:
def id(self) -> int:
return self.uid

def to_dict(self) -> dict:
return {
"id": self.uid,
"name": self.name,
"avatar": self.avatar,
"bio": self.bio,
"username": self.username,
"email": self.email,
}


class Chatbot(db.Model):
__tablename__ = "chatbots"
Expand Down
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function App() {
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/hub" element={<HubPage />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/profile/:id" element={<UserProfilePage />} />
<Route path="/profile/:username" element={<ProfilePage />} />
<Route path="/chatbot/:id" element={<ChatbotPage />} />
<Route path="/anonymous" element={<AnonymousPage />} />
<Route path="/imagine" element={<ImaginePage />} />
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default function Navbar() {
) : (
<>
<li>
<Link to={"/profile"}>
<Link to={`/profile/${user.username}`}>
<img
src={user.avatar}
alt="User Avatar"
Expand Down
3 changes: 1 addition & 2 deletions client/src/components/modals/update-chatbot-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { useState } from "react";

import {
Expand Down Expand Up @@ -56,7 +55,7 @@ export default function UpdateChatbotModal() {
{ headers: authHeaders }
);
if (response.data?.success) {
toast.success("Created!");
toast.success("Updated!");
modal.onClose();
form.reset();
rq.invalidateQueries({ queryKey: ["dashboard"] });
Expand Down
157 changes: 157 additions & 0 deletions client/src/components/modals/update-profile-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { useState } from "react";

import {
AlertDialog,
AlertDialogContent,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogFooter,
AlertDialogCancel,
} from "../ui/alert-dialog";
import axios from "axios";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { useUpdateProfileModal } from "@/stores/modal-store";
import toast from "react-hot-toast";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { updateProfileSchema } from "@/lib/schemas";
import { zodResolver } from "@hookform/resolvers/zod";
import { SERVER_URL } from "@/lib/utils";
import { Loader2 } from "lucide-react";
import { Textarea } from "../ui/textarea";
import { authHeaders } from "@/lib/queries";
import { useQueryClient } from "@tanstack/react-query";

export default function UpdateProfileModal() {
const modal = useUpdateProfileModal();

const { prevName, prevBio, prevUsername } = modal.extras;

const [loading, setLoading] = useState(false); // Loading state for request
const rq = useQueryClient();
const form = useForm<z.infer<typeof updateProfileSchema>>({
resolver: zodResolver(updateProfileSchema),
defaultValues: {
name: "",
bio: "",
username: "",
},
});

async function onSubmit(values: z.infer<typeof updateProfileSchema>) {
try {
setLoading(true);
const response = await axios.post(
`${SERVER_URL}/api/profile/edit`,
values,
{ headers: authHeaders }
);
if (response.data?.success) {
toast.success("Updated!");
modal.onClose();
form.reset();
rq.invalidateQueries({ queryKey: ["user", values.username] });
} else {
throw new Error(response.data?.message || "failed. Please try again.");
}
} catch (error: any) {
const errorMessage =
error.response?.data?.message ||
error.message ||
"An unexpected error occurred.";
toast.error(errorMessage);
console.log("[UPDATING_ERROR]", error);
} finally {
setLoading(false);
}
}

if (form.getValues().name === "") {
form.reset({
name: prevName,
bio: prevBio,
username: prevUsername,
});
}

return (
<AlertDialog open={modal.isOpen} onOpenChange={() => modal.onClose()}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="text-xl font-bold mb-4">
Update The Profile
</AlertDialogTitle>
</AlertDialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8 w-full"
>
<FormField
control={form.control}
disabled={loading}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>

<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
disabled={loading}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>

<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
disabled={loading}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>

<FormMessage />
</FormItem>
)}
/>
<Button className="w-full" type="submit" disabled={loading}>
{loading ? <Loader2 className="animate-spin" /> : "Save"}
</Button>
</form>
</Form>
<AlertDialogFooter>
<AlertDialogCancel className="w-full" disabled={loading}>
Cancel
</AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
8 changes: 0 additions & 8 deletions client/src/contexts/auth-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ import axios from "axios";
import { SERVER_URL } from "@/lib/utils";
import { useNavigate } from "react-router-dom"; // Import useNavigate

interface User {
name: string;
avatar: string;
username: string;
email: string;
bio?: string;
}

interface AuthContextType {
user: User | null;
login: (username: string, password: string) => Promise<void>;
Expand Down
2 changes: 2 additions & 0 deletions client/src/contexts/modals.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import CreateChatbotModal from "@/components/modals/create-chatbot-modal";
import DeleteChatbotModal from "@/components/modals/delete-chatbot-modal";
import UpdateChatbotModal from "@/components/modals/update-chatbot-modal";
import UpdateProfileModal from "@/components/modals/update-profile-modal";

export default function Modals() {
return (
<>
<CreateChatbotModal />
<UpdateChatbotModal />
<UpdateProfileModal />
<DeleteChatbotModal />
</>
);
Expand Down
16 changes: 16 additions & 0 deletions client/src/lib/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ interface HubResponse {
images: ImageGen[];
}

interface ProfileResponse {
user: User;
bots: Chatbot[];
images: ImageGen[];
}

const token = localStorage.getItem("token");

export const authHeaders = {
Authorization: `Bearer ${token}`,
};
Expand All @@ -41,6 +48,15 @@ export const fetchHubData = async (): Promise<HubResponse | undefined> => {
return data;
};

export const fetchProfileData = async (
username: string
): Promise<ProfileResponse> => {
const { data } = await axios.get(`${SERVER_URL}/api/user/${username}`, {
headers: authHeaders,
});
return data;
};

export const fetchChatbotData = async (
id: string
): Promise<ChatbotResponse> => {
Expand Down
12 changes: 12 additions & 0 deletions client/src/lib/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ export const createChatbotSchema = z.object({
}),
});

export const updateProfileSchema = z.object({
name: z.string().min(3, {
message: "Name should be at least 3 characters long.",
}),
bio: z.string().min(3, {
message: "Bio should be at least 3 characters long.",
}),
username: z.string().min(3, {
message: "Username should be at least 3 characters long.",
}),
});

export const messageSchema = z.object({
query: z.string().min(3, {
message: "Query should be at least 3 characters long.",
Expand Down
4 changes: 2 additions & 2 deletions client/src/pages/Hub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function HubPage() {
);
}

function ChatbotCard({ chatbot }: { chatbot: Chatbot }) {
export function ChatbotCard({ chatbot }: { chatbot: Chatbot }) {
return (
<>
<div className="min-w-80 bg-light dark:bg-dark p-6 rounded-lg transition-all drop-shadow hover:shadow border border-lighter dark:border-darker flex flex-col justify-between h-64">
Expand Down Expand Up @@ -73,7 +73,7 @@ function ChatbotCard({ chatbot }: { chatbot: Chatbot }) {
);
}

function ImageCard({ image }: { image: ImageGen }) {
export function ImageCard({ image }: { image: ImageGen }) {
return (
<>
<div className="flex justify-start items-start space-x-2 mb-2">
Expand Down
Loading

0 comments on commit b99183f

Please sign in to comment.