Skip to content

Commit c365868

Browse files
committed
update verifed badge, add custom user component, add user setting in setting modal
1 parent 1241ecc commit c365868

7 files changed

Lines changed: 171 additions & 26 deletions

File tree

public/badge/verifed.png

19.8 KB
Loading

src/app/(main)/_components/navigation.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ export function Navigation() {
178178
return (
179179
<>
180180
<aside ref={sidebarRef} className={cn(
181-
"group/sidebar h-full bg-[#fcfcfc] dark:bg-[#111111] overflow-y-auto relative flex w-60 flex-col z-[99999]",
181+
"group/sidebar h-full bg-[#fcfcfc] dark:bg-[#111111] overflow-y-auto relative flex w-60 flex-col z-50",
182182
isResetting && "transition-all ease-in-out duration-300",
183183
isMobile && "w-0"
184184
)}>
@@ -236,9 +236,6 @@ export function Navigation() {
236236
)}
237237
</div>
238238

239-
<div className="absolute bottom-4 left-0 w-full flex justify-center items-center">
240-
<Link href={pages.PROFILE(isOrg, isOrg ? organization?.slug ?? "" : user?.username ?? "")} className="text-sm text-primary/50 hover:text-primary/80 transition-all duration-200">Перейти в профиль</Link>
241-
</div>
242239
<div onMouseDown={handleMouseDown} onClick={resetWidth} className="opacity-0 group-hover/sidebar:opacity-100 transition cursor-ew-resize absolute h-full w-1 bg-primary/10 right-0 top-0" />
243240
</aside>
244241

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,54 @@
1-
import { OrganizationSwitcher } from "@clerk/clerk-react"
1+
"use client"
2+
3+
import { OrganizationSwitcher, useOrganization, useUser } from "@clerk/clerk-react"
24
import { pages } from "@/config/routing/pages.route"
5+
import Link from "next/link"
6+
import * as React from "react"
7+
import {
8+
DropdownMenu,
9+
DropdownMenuTrigger,
10+
DropdownMenuContent,
11+
DropdownMenuSeparator,
12+
} from "@/components/ui/dropdown-menu"
13+
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
14+
import { ChevronRight } from "lucide-react"
315

416
export function UserItem(){
17+
const { user } = useUser()
18+
const { organization } = useOrganization()
19+
const isOrg = organization?.id !== undefined
20+
21+
const image = (user as any)?.imageUrl || (user as any)?.profileImageUrl || (user as any)?.image || null
22+
523
return (
6-
<div className="flex items-center m-2">
7-
<OrganizationSwitcher afterSelectOrganizationUrl={pages.DASHBOARD()}/>
24+
<div>
25+
<DropdownMenu>
26+
<DropdownMenuTrigger asChild>
27+
<button className="flex items-center w-full gap-2 p-2 hover:bg-accent">
28+
<Avatar className="h-6 w-6">
29+
{image ? (
30+
<AvatarImage src={image} alt={user?.username ?? user?.fullName ?? "user"} className="object-cover" />
31+
) : (
32+
<AvatarFallback className="text-sm">{(user?.username || "?").charAt(0).toUpperCase()}</AvatarFallback>
33+
)}
34+
</Avatar>
35+
<div className="text-sm text-muted-foreground font-medium truncate max-w-[8rem]">{user?.username ?? user?.fullName ?? "Пользователь"}</div>
36+
</button>
37+
</DropdownMenuTrigger>
38+
39+
<DropdownMenuContent className="w-52 p-3 flex flex-col items-start ml-2" align="start">
40+
<OrganizationSwitcher afterSelectOrganizationUrl={pages.DASHBOARD()} />
41+
42+
<DropdownMenuSeparator/>
43+
44+
<Link
45+
href={pages.PROFILE(isOrg, isOrg ? organization?.slug ?? "" : user?.username ?? "")}
46+
className="text-sm text-primary/65 flex flex-row hover:text-primary/80"
47+
>
48+
Перейти в профиль <ChevronRight className="w-5 h-5"/>
49+
</Link>
50+
</DropdownMenuContent>
51+
</DropdownMenu>
852
</div>
953
)
1054
}

src/app/(profile)/_components/verifed.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Check } from 'lucide-react'
21
import { useState } from 'react'
32
import type { VerifedBadgeProps } from "@/config/types/profile.types";
3+
import Image from 'next/image';
44

55
export default function VerifedBadge({ text, size, clicked = false, down = false }: VerifedBadgeProps) {
66
const [isClicked, setIsClicked] = useState(false)
@@ -22,9 +22,13 @@ export default function VerifedBadge({ text, size, clicked = false, down = false
2222
className="relative select-none sm:block group"
2323
onMouseLeave={handleMouseLeave}
2424
>
25-
<Check
25+
<Image
2626
className={`w-${size} h-${size} text-yellow-400 transform transition-transform duration-200 hover:scale-110 cursor-pointer`}
2727
onClick={handleClick}
28+
alt='verifed badge'
29+
width={200}
30+
height={200}
31+
src={"/badge/verifed.png"}
2832
/>
2933
<span
3034
className={`absolute ${down ? "top-5 ml-8" : "-top-8"} left-1/2 -translate-x-1/2 bg-black px-2 py-1 rounded text-xs transition-opacity duration-200 text-center whitespace-nowrap text-yellow-200

src/app/(profile)/org/[orgname]/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,13 @@ export default function OrgProfile({ params }: OrgProps) {
131131
<span className="flex flex-row items-center gap-2">
132132
{org?.name}
133133
{org?.badges.verified && (
134-
<VerifedBadge text="Верефицированная команда" size={7} clicked={false} />
134+
<VerifedBadge text="Верефицированная команда" size={6} clicked={false} />
135135
)}
136136
</span>
137137
</span>
138138
<div className="hidden sm:block">
139139
{org?.badges.verified && (
140-
<VerifedBadge text="Верефицированная команда" size={7} clicked={false} />
140+
<VerifedBadge text="Верефицированная команда" size={6} clicked={false} />
141141
)}
142142
</div>
143143
</h1>
@@ -198,7 +198,7 @@ export default function OrgProfile({ params }: OrgProps) {
198198
</>
199199
)}
200200
<div className="mt-8 mx-6">
201-
<h2 className="text-2xl font-bold mb-4">All Notes</h2>
201+
<h2 className="text-2xl font-bold mb-4">Заметки</h2>
202202
{org._id ? (
203203
<DocumentList user={org} profile={params.orgname} setProfile={setOrg} />
204204
) : (
@@ -243,7 +243,7 @@ export default function OrgProfile({ params }: OrgProps) {
243243

244244
{!org?.privated && (
245245
<div className="mt-8 mx-6">
246-
<h2 className="text-2xl font-bold mb-4">All Notes</h2>
246+
<h2 className="text-2xl font-bold mb-4">Заметки</h2>
247247
{org._id ? (
248248
<DocumentList user={org} profile={params.orgname} setProfile={setOrg} />
249249
) : (

src/app/(profile)/user/[username]/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export default function UserProfile({ params }: UsernameProps) {
199199
</>
200200
)}
201201
<div className="mt-8 mx-6">
202-
<h2 className="text-2xl font-bold mb-4">All Notes</h2>
202+
<h2 className="text-2xl font-bold mb-4">Заметки</h2>
203203
{profile._id ? (
204204
<DocumentList user={profile} profile={params.username} setProfile={setProfile} />
205205
) : (
@@ -244,7 +244,7 @@ export default function UserProfile({ params }: UsernameProps) {
244244

245245
{!profile?.privated && (
246246
<div className="mt-8 mx-6">
247-
<h2 className="text-2xl font-bold mb-4">All Notes</h2>
247+
<h2 className="text-2xl font-bold mb-4">Заметки</h2>
248248
{profile._id ? (
249249
<DocumentList user={profile} profile={params.username} setProfile={setProfile} />
250250
) : (

src/components/modal/settings-modal.tsx

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@ import { ModeToggle } from "../mode-toggle"
33
import { useSettings } from "../hooks/use-settings"
44
import { Label } from "../ui/label"
55
import { Separator } from "@radix-ui/react-dropdown-menu"
6-
import { SignOutButton, useOrganization } from "@clerk/nextjs"
6+
import { SignOutButton, useOrganization, useClerk, useUser } from "@clerk/nextjs"
77
import { useRouter } from "next/navigation"
8-
import { useUser } from "@clerk/nextjs"
98
import { useState, useEffect } from "react"
109
import { Switch } from "@/components/ui/switch"
1110
import { getById as getUserById } from "../../app/api/users/user"
1211
import { getById as getOrgById } from "../../app/api/orgs/org"
1312
import { updateUser } from "../../app/api/users/user"
1413
import { updateOrg } from "../../app/api/orgs/org"
1514
import { pages } from "@/config/routing/pages.route"
15+
import { Button } from "../ui/button"
16+
import { useRef } from "react"
1617

1718
export function SettingsModal() {
1819
const settings = useSettings()
1920
const router = useRouter()
2021
const { user } = useUser()
22+
const clerk = useClerk()
2123
const { organization } = useOrganization()
2224
const [isPrivated, setIsPrivated] = useState<boolean>(false)
2325
const [watermark, setWatermark] = useState<boolean>(false)
@@ -66,6 +68,84 @@ export function SettingsModal() {
6668
}
6769
}
6870

71+
// backdrop modal (simplified)
72+
73+
const backdropId = "clerk-backdrop-overlay"
74+
const intervalRef = useRef<number | null>(null)
75+
76+
const addBackdrop = () => {
77+
if (typeof document === "undefined") return
78+
if (document.getElementById(backdropId)) return
79+
80+
const el = document.createElement("div")
81+
el.id = backdropId
82+
Object.assign(el.style, {
83+
position: "fixed",
84+
inset: "0",
85+
background: "rgba(0,0,0,0.45)",
86+
zIndex: "9998",
87+
pointerEvents: "auto",
88+
})
89+
90+
document.body.appendChild(el)
91+
}
92+
93+
const removeBackdrop = () => {
94+
if (typeof document === "undefined") return
95+
const el = document.getElementById(backdropId)
96+
if (el) el.remove()
97+
}
98+
99+
const watchForClerkModal = () => {
100+
if (typeof document === "undefined") return
101+
102+
const selectors = [
103+
'[data-clerk-portal]',
104+
'[data-clerk-root]',
105+
'.clerk-root',
106+
'.clerk-modal',
107+
'[id^="clerk"]',
108+
'[id^="__clerk"]',
109+
]
110+
111+
const hasClerk = () => selectors.some(s => !!document.querySelector(s))
112+
113+
const start = Date.now()
114+
if (intervalRef.current) return
115+
116+
intervalRef.current = window.setInterval(() => {
117+
if (!hasClerk() || Date.now() - start > 60000) {
118+
removeBackdrop()
119+
if (intervalRef.current) {
120+
clearInterval(intervalRef.current)
121+
intervalRef.current = null
122+
}
123+
}
124+
}, 200)
125+
}
126+
127+
useEffect(() => {
128+
if (!settings.isOpen) {
129+
removeBackdrop()
130+
if (intervalRef.current) {
131+
clearInterval(intervalRef.current)
132+
intervalRef.current = null
133+
}
134+
}
135+
}, [settings.isOpen])
136+
137+
useEffect(() => {
138+
return () => {
139+
removeBackdrop()
140+
if (intervalRef.current) {
141+
clearInterval(intervalRef.current)
142+
intervalRef.current = null
143+
}
144+
}
145+
}, [])
146+
147+
// backdrop modal end
148+
69149
return (
70150
<Dialog open={settings.isOpen} onOpenChange={settings.onClose}>
71151
<DialogContent>
@@ -115,15 +195,35 @@ export function SettingsModal() {
115195
)}
116196

117197
<Separator />
118-
<div
119-
onClick={() => {
120-
router.push(pages.ROOT)
121-
settings.onClose()
122-
}}
123-
className="m-auto hover:text-primary/80"
124-
>
125-
<SignOutButton>Выйти из аккаунта</SignOutButton>
126-
</div>
198+
199+
<section className="flex flex-col md:flex-row justify-center items-center gap-3">
200+
<div className="w-full flex justify-center md:w-auto">
201+
<Button
202+
onClick={() => {
203+
addBackdrop()
204+
watchForClerkModal()
205+
clerk?.openUserProfile?.()
206+
settings.onClose()
207+
}}
208+
variant={"outline"}
209+
>
210+
Настройки аккаунта
211+
</Button>
212+
</div>
213+
<div
214+
onClick={() => {
215+
router.push(pages.ROOT)
216+
settings.onClose()
217+
}}
218+
className="w-full flex justify-center md:w-auto hover:text-primary/80"
219+
>
220+
<SignOutButton>
221+
<Button variant={"destructive"}>
222+
Выйти из аккаунта
223+
</Button>
224+
</SignOutButton>
225+
</div>
226+
</section>
127227
</DialogContent>
128228
</Dialog>
129229
)

0 commit comments

Comments
 (0)