Onboarding (#155)

* onboarding layout

* edit profile avatar

* onboarding tasks
This commit is contained in:
olya
2024-09-09 12:49:46 +03:00
committed by GitHub
parent 45620c4424
commit 66c96efad8
4 changed files with 122 additions and 58 deletions

View File

@@ -0,0 +1,5 @@
import OnboardingRoute from "@/components/routes/OnboardingRoute"
export default function EditProfilePage() {
return <OnboardingRoute />
}

View File

@@ -1,12 +1,13 @@
"use client"
import { useAccount } from "@/lib/providers/jazz-provider"
import { useUser } from "@clerk/nextjs"
import { useState, useRef, useCallback } from "react"
import { useParams } from "next/navigation"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import Link from "next/link"
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
import { Avatar, AvatarImage } from "@/components/ui/avatar"
interface ProfileStatsProps {
number: number
@@ -26,8 +27,20 @@ export const ProfileWrapper = () => {
const account = useAccount()
const params = useParams()
const username = params.username as string
const { user, isSignedIn } = useUser()
const avatarInputRef = useRef<HTMLInputElement>(null)
const editAvatar = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
console.log("File selected:", file)
const imageUrl = URL.createObjectURL(file)
if (account.me && account.me.profile) {
account.me.profile.avatarUrl = imageUrl
}
}
}
const [isEditing, setIsEditing] = useState(false)
const [newName, setNewName] = useState(account.me?.profile?.name || "")
const [error, setError] = useState("")
@@ -72,13 +85,6 @@ export const ProfileWrapper = () => {
setError("")
}
const editAvatar = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]
if (file) {
console.log("File selected:", file)
}
}
if (!account.me || !account.me.profile) {
return (
<div className="flex h-screen flex-col py-3 text-black dark:text-white">
@@ -107,14 +113,12 @@ export const ProfileWrapper = () => {
<p className="text-2xl font-semibold">{username}</p>
<div className="flex flex-col items-center border-b border-neutral-900 bg-inherit pb-5">
<div className="flex w-full max-w-2xl align-top">
<Avatar className="mr-3 h-[130px] w-[130px] hover:opacity-80">
<AvatarImage src={account.me?.profile?.avatarUrl} alt={account.me?.profile?.name} />
<AvatarFallback onClick={() => avatarInputRef.current?.click()} className="cursor-pointer">
{account.me?.profile?.name?.charAt(0)}
</AvatarFallback>
</Avatar>
<Button onClick={() => avatarInputRef.current?.click()} variant="ghost" className="p-0 hover:bg-transparent">
<Avatar className="size-20">
<AvatarImage src={account.me?.profile?.avatarUrl || user?.imageUrl} alt={user?.fullName || ""} />
</Avatar>
</Button>
<input type="file" ref={avatarInputRef} onChange={editAvatar} accept="image/*" style={{ display: "none" }} />
<div className="ml-6 flex-1">
{isEditing ? (
<>
@@ -161,46 +165,3 @@ export const ProfileWrapper = () => {
</div>
)
}
{
// interface ProfileLinksProps {
// linklabel?: string
// link?: string
// topic?: string
// }
// interface ProfilePagesProps {
// topic?: string
// }
// const ProfileLinks: React.FC<ProfileLinksProps> = ({ linklabel, link, topic }) => {
// return (
// <div className="flex flex-row items-center justify-between bg-[#121212] p-3 text-black dark:text-white">
// <div className="flex flex-row items-center space-x-3">
// <p className="text-base text-opacity-90">{linklabel || "Untitled"}</p>
// <div className="flex cursor-pointer flex-row items-center gap-1">
// <LaIcon name="Link" />
// <p className="text-sm text-opacity-10">{link || "#"}</p>
// </div>
// </div>
// <div className="text0opacity-50 bg-[#1a1a1a] p-2">{topic || "Uncategorized"}</div>
// </div>
// )
// }
// const ProfilePages: React.FC<ProfilePagesProps> = ({ topic }) => {
// return (
// <div className="flex flex-row items-center justify-between rounded-lg bg-[#121212] p-3 text-black dark:text-white">
// <div className="rounded-lg bg-[#1a1a1a] p-2 text-opacity-50">{topic || "Uncategorized"}</div>
// </div>
// )
// }
/* <a href={account.me.root?.website || "#"} className="mb-1 flex flex-row items-center text-sm font-light">
<Icon name="Link" />
<p className="pl-1">{account.me.root?.website}</p>
</a> */
/* <Button
onClick={clickEdit}
className="shadow-outer ml-auto flex h-[34px] cursor-pointer flex-row space-x-2 rounded-lg bg-white px-3 text-black shadow-[1px_1px_1px_1px_rgba(0,0,0,0.3)] hover:bg-black/10 dark:bg-[#222222] dark:text-white dark:hover:opacity-60"
>
<LaIcon name="UserCog" className="text-foreground cursor-pointer" />
<span>Edit Profile</span>
</Button> */
}

View File

@@ -66,6 +66,15 @@ export const ProfileSection: React.FC = () => {
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/onboarding">
<div className="relative flex flex-1 items-center gap-2">
<LaIcon name="LayoutList" />
<span className="line-clamp-1 flex-1">Onboarding</span>
</div>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => signOut()}>
<div className="relative flex flex-1 items-center gap-2">
<LaIcon name="LogOut" />

View File

@@ -0,0 +1,89 @@
"use client"
import { LaIcon } from "../custom/la-icon"
import { useState } from "react"
const steps = [
{
number: 1,
title: "Create Link",
description:
"Links are essentially bookmarks of things from internet. You can create a link by pressing Links button in left sidebar. Then pressing + button on the bottom.",
task: "create any Link with any title or description (for example, you can add https://learn-anything.xyz as link)"
},
{
number: 2,
title: "Create Page",
description:
"Pages are things with content inside (images, text, anything). You can think of them as Notion pages. To create page, press the + button next to pages, then create title and put some content.",
task: "create any Page with any content inside"
},
{
number: 3,
title: "Start tracking Learning status of some Topic",
description:
"What makes Learn Anything different from Notion and other tools is notion of topics. A topic is anything after learn-anything.xyz/<topic>, for example learn-anything.xyz/typescript. You can go to the page, then on top right corner where it says add to my profile, press it and change the state of the topic to I want to learn, Learning or Learned.",
task: "go to any Topic, and mark it as I want to learn"
},
{
number: 4,
title: "Add a Link from a Topic into personal link collection",
description:
"If you noticed, there are links attached to topics as a list. This is the topic's study guide. It will be improved greatly in future and we will allow any user to edit these study guides too (Reddit style). You can click on the circle to left of the links and add a link to your personal collection with learning status too.",
task: "add any Link from topic typescript into your personal collection"
}
]
const StepItem = ({
number,
title,
description,
task,
done
}: {
number: number
title: string
description: string
task: string
done: boolean
}) => (
<div className="flex items-start space-x-4 py-4">
<div className="border-foreground/20 w-6 flex-shrink-0 items-center justify-center rounded-3xl border text-center opacity-70">
{number}
</div>
<div className="flex-grow space-y-2">
<h3 className="font-semibold">{title}</h3>
<p className="w-[90%] leading-relaxed opacity-70">{description}</p>
<div className="flex flex-row items-center gap-2">
<LaIcon name={done ? "SquareCheck" : "Square"} className={` ${done ? "text-green-500" : ""}`} />
<p>{task}</p>
</div>
</div>
</div>
)
export default function OnboardingRoute() {
const [completedSteps, setCompletedSteps] = useState<number[]>([])
const stepComplete = (stepNumber: number) => {
setCompletedSteps(prev =>
prev.includes(stepNumber) ? prev.filter(num => num !== stepNumber) : [...prev, stepNumber]
)
}
return (
<div className="flex flex-1 flex-col space-y-8 text-sm text-black dark:text-white">
<div className="ml-10 flex flex-col items-start border-b border-neutral-200 bg-inherit dark:border-neutral-900">
<p className="h-[74px] p-[20px] text-2xl font-semibold opacity-60">Onboarding</p>
</div>
<div className="mx-auto w-[70%] rounded-lg border border-neutral-200 bg-inherit p-6 shadow dark:border-neutral-900">
<h2 className="mb-4 text-lg font-semibold">Complete the steps below to get started</h2>
<div className="divide-y">
{steps.map(step => (
<StepItem key={step.number} {...step} done={step.number === 1} />
))}
</div>
</div>
</div>
)
}