+
+
diff --git a/src/app/members/[username]/page.tsx b/src/app/members/[username]/page.tsx
new file mode 100644
index 0000000..60e2a50
--- /dev/null
+++ b/src/app/members/[username]/page.tsx
@@ -0,0 +1,10 @@
+import { redirect } from "next/navigation";
+
+export default async function LegacyPublicMemberProfilePage({
+ params,
+}: {
+ params: Promise<{ username: string }>;
+}) {
+ const { username } = await params;
+ redirect(`/users/profiles/${encodeURIComponent(username)}`);
+}
diff --git a/src/app/profile/edit/page.tsx b/src/app/profile/edit/page.tsx
new file mode 100644
index 0000000..a24a220
--- /dev/null
+++ b/src/app/profile/edit/page.tsx
@@ -0,0 +1,11 @@
+import type { Metadata } from "next";
+import ProfileEdit from "@/views/ProfileEdit";
+
+export const metadata: Metadata = {
+ title: "ویرایش پروفایل",
+ robots: { index: false, follow: false },
+};
+
+export default function ProfileEditPage() {
+ return
;
+}
diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx
index 306086a..f2f9c46 100644
--- a/src/app/profile/page.tsx
+++ b/src/app/profile/page.tsx
@@ -6,6 +6,11 @@ export const metadata: Metadata = {
robots: { index: false, follow: false },
};
-export default function ProfilePage() {
- return
;
+type SearchParams = Promise
>;
+
+export default async function ProfilePage({ searchParams }: { searchParams: SearchParams }) {
+ const params = await searchParams;
+ const tab = Array.isArray(params.tab) ? params.tab[0] : params.tab;
+
+ return ;
}
diff --git a/src/app/users/profiles/[username]/page.tsx b/src/app/users/profiles/[username]/page.tsx
new file mode 100644
index 0000000..24e22ad
--- /dev/null
+++ b/src/app/users/profiles/[username]/page.tsx
@@ -0,0 +1,11 @@
+import type { Metadata } from "next";
+import PublicProfile from "@/views/PublicProfile";
+
+export const metadata: Metadata = {
+ title: "پروفایل عمومی عضو",
+ description: "پروفایل عمومی اعضای انجمن علمی مهندسی کامپیوتر دانشگاه گیلان.",
+};
+
+export default function PublicMemberProfilePage() {
+ return ;
+}
diff --git a/src/components/AsyncSearchableCombobox.tsx b/src/components/AsyncSearchableCombobox.tsx
index c723af2..3fa203b 100644
--- a/src/components/AsyncSearchableCombobox.tsx
+++ b/src/components/AsyncSearchableCombobox.tsx
@@ -21,7 +21,9 @@ export type AsyncComboboxOption = {
};
type AsyncSearchableComboboxProps = {
+ id?: string;
value?: string | null;
+ selectedLabel?: string | null;
onChange: (value: string | null) => void;
loadOptions: (params: { search: string; limit: number; offset: number }) => Promise<{
count: number;
@@ -37,7 +39,9 @@ type AsyncSearchableComboboxProps = {
};
export default function AsyncSearchableCombobox({
+ id,
value,
+ selectedLabel,
onChange,
loadOptions,
placeholder = "انتخاب کنید",
@@ -97,19 +101,27 @@ export default function AsyncSearchableCombobox({
-
-
+
+
{loading ? (
diff --git a/src/components/MobileVerificationGate.tsx b/src/components/MobileVerificationGate.tsx
index 5342547..75f64db 100644
--- a/src/components/MobileVerificationGate.tsx
+++ b/src/components/MobileVerificationGate.tsx
@@ -9,13 +9,10 @@ import OtpCodeField from "@/components/OtpCodeField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
-import { resolveErrorMessage } from "@/lib/utils";
+import { toEnglishDigits } from "@/lib/digits";
+import { resolveErrorMessage, toPersianDigits } from "@/lib/utils";
-const toEnglishDigits = (value: string) =>
- value
- .replace(/[\u06F0-\u06F9]/g, (digit) => String(digit.charCodeAt(0) - 0x06f0))
- .replace(/[\u0660-\u0669]/g, (digit) => String(digit.charCodeAt(0) - 0x0660))
- .replace(/[^\d]/g, "");
+const normalizeNumericInput = (value: string) => toEnglishDigits(value).replace(/[^\d]/g, "");
export default function MobileVerificationGate() {
const { user, isAuthenticated, loading, refreshProfile, setUser, logout } = useAuth();
@@ -29,7 +26,7 @@ export default function MobileVerificationGate() {
useEffect(() => {
if (user?.mobile) {
- setMobile(user.mobile);
+ setMobile(normalizeNumericInput(user.mobile));
setStep("verify");
}
}, [user?.mobile]);
@@ -45,7 +42,7 @@ export default function MobileVerificationGate() {
const handleSendOtp = async () => {
try {
setSending(true);
- const normalizedMobile = toEnglishDigits(mobile);
+ const normalizedMobile = normalizeNumericInput(mobile);
const response = await api.sendMobileVerificationOtp({ mobile: normalizedMobile });
setMobile(normalizedMobile);
setStep("verify");
@@ -70,8 +67,8 @@ export default function MobileVerificationGate() {
try {
setVerifying(true);
const profile = await api.verifyMobile({
- mobile: toEnglishDigits(mobile),
- code: toEnglishDigits(code),
+ mobile: normalizeNumericInput(mobile),
+ code: normalizeNumericInput(code),
});
setUser(profile);
await refreshProfile();
@@ -143,8 +140,8 @@ export default function MobileVerificationGate() {
type="tel"
dir="ltr"
inputMode="numeric"
- value={mobile}
- onChange={(event) => setMobile(toEnglishDigits(event.target.value))}
+ value={toPersianDigits(mobile)}
+ onChange={(event) => setMobile(normalizeNumericInput(event.target.value))}
placeholder="09xxxxxxxxx"
className="h-12 rounded-2xl"
disabled={sending || verifying}
@@ -157,7 +154,7 @@ export default function MobileVerificationGate() {
setCode(toEnglishDigits(value))}
+ onChange={(value) => setCode(normalizeNumericInput(value))}
disabled={verifying}
/>
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index a6ff5c5..31613e0 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -103,7 +103,7 @@ function ProfileAvatarMenu() {
) : null}