From ef2e86861a8d3c77bd148191baefaa17657c3249 Mon Sep 17 00:00:00 2001
From: payacom <59262811+payacom@users.noreply.github.com>
Date: Thu, 13 Aug 2026 13:09:25 +0330
Subject: [PATCH] instagram
---
.../(login)/forget-password-otp/page.tsx | 3 -
.../(login)/login-with-username/page.tsx | 3 -
src/app/(auth)/(login)/login/page.tsx | 13 +-
src/app/(auth)/(login)/verify-otp/page.tsx | 3 -
.../(auth)/(register)/register-otp/page.tsx | 3 -
.../(register)/register/fullname/page.tsx | 125 +++++++++----
src/app/(auth)/(register)/register/page.tsx | 13 +-
src/app/(auth)/verify/national-cart/page.tsx | 12 +-
src/app/about-us/page.tsx | 2 +-
src/app/auth/instagram/callback/page.tsx | 4 +
src/app/instagram-data-deletion/page.tsx | 40 +++++
src/app/offer/[id]/page.tsx | 25 +--
src/app/privacy/page.tsx | 45 +++++
.../settings/edit/instagram-import/page.tsx | 100 +++++++++++
.../edit/instagram-import/select/page.tsx | 168 ++++++++++++++++++
src/components/academy/MainModelCard.tsx | 20 +--
src/components/auth/AuthLanguageToggle.tsx | 37 ++++
src/components/auth/GoogleSignInButton.tsx | 11 --
src/components/auth/InstagramSignInButton.tsx | 14 --
src/components/main/ScoreRateStats.tsx | 29 +++
.../models/MainModelCard/MainModelCard.tsx | 20 +--
.../models/ModelPage/MainModelCardPost.tsx | 20 +--
.../models/ModelPage/ModelContent.tsx | 1 +
.../models/ModelPage/ModelContentPosts.tsx | 27 +--
.../models/ModelPage/ModelHeadRowTwo.tsx | 27 +--
.../Workroom/ProjectRequestsAction.tsx | 51 ++----
.../Workroom/ProjectWorkroomSelectedUser.tsx | 33 +---
src/components/search/SearchCard.tsx | 15 +-
src/config/instagramAuth.ts | 9 +-
src/locales/en/common.json | 30 +++-
src/locales/fa/common.json | 30 +++-
31 files changed, 636 insertions(+), 297 deletions(-)
create mode 100644 src/app/instagram-data-deletion/page.tsx
create mode 100644 src/app/privacy/page.tsx
create mode 100644 src/app/settings/edit/instagram-import/select/page.tsx
create mode 100644 src/components/auth/AuthLanguageToggle.tsx
create mode 100644 src/components/main/ScoreRateStats.tsx
diff --git a/src/app/(auth)/(login)/forget-password-otp/page.tsx b/src/app/(auth)/(login)/forget-password-otp/page.tsx
index ab36360..1d03e76 100644
--- a/src/app/(auth)/(login)/forget-password-otp/page.tsx
+++ b/src/app/(auth)/(login)/forget-password-otp/page.tsx
@@ -4,7 +4,6 @@
import AuthButton from "@/components/auth/AuthButton";
import AuthHead from "@/components/auth/AuthHead";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
import Link from "next/link";
import React, { useMemo, useState } from "react";
import AuthOtpInput from "@/components/auth/AuthOtpInput";
@@ -22,7 +21,6 @@ import LocalePageShell from "@/components/i18n/LocalePageShell";
function ForgetPasswordOtp() {
const { t } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const [otpExpired, setOtpExpired] = useState(false);
const [resendLoading, setResendLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
@@ -139,7 +137,6 @@ function ForgetPasswordOtp() {
{t("auth.editMobile")}
-
diff --git a/src/app/(auth)/(login)/login-with-username/page.tsx b/src/app/(auth)/(login)/login-with-username/page.tsx
index 203b787..ef0a26b 100644
--- a/src/app/(auth)/(login)/login-with-username/page.tsx
+++ b/src/app/(auth)/(login)/login-with-username/page.tsx
@@ -6,7 +6,6 @@ import AuthHead from "@/components/auth/AuthHead";
import AuthInput from "@/components/auth/AuthInput";
import AuthPasswordInput from "@/components/auth/AuthPasswordInput";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
import Link from "next/link";
import React, { useMemo, useState } from "react";
import useAxios from "@/hooks/useAxios";
@@ -27,7 +26,6 @@ import LocalePageShell from "@/components/i18n/LocalePageShell";
function LoginWithUsername() {
const { t, i18n } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [authError, setAuthError] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
@@ -176,7 +174,6 @@ function LoginWithUsername() {
{t("auth.signUp")}
-
diff --git a/src/app/(auth)/(login)/login/page.tsx b/src/app/(auth)/(login)/login/page.tsx
index e4d885f..43d75a2 100644
--- a/src/app/(auth)/(login)/login/page.tsx
+++ b/src/app/(auth)/(login)/login/page.tsx
@@ -4,9 +4,9 @@ import AuthButton from "@/components/auth/AuthButton";
import AuthHead from "@/components/auth/AuthHead";
import AuthInput from "@/components/auth/AuthInput";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
+import AuthLanguageToggle from "@/components/auth/AuthLanguageToggle";
import Link from "next/link";
-import React, { useMemo, useState } from "react";
+import React, { useMemo } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
@@ -22,7 +22,6 @@ import LocalePageShell from "@/components/i18n/LocalePageShell";
function Login() {
const { t } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const { request, loading } = useAxios();
useAuthSessionRedirect();
const redirectPath = getSafeRedirectPath();
@@ -62,7 +61,8 @@ function Login() {
return (
-
+
diff --git a/src/app/(auth)/(login)/verify-otp/page.tsx b/src/app/(auth)/(login)/verify-otp/page.tsx
index c0503b1..2cb8bc8 100644
--- a/src/app/(auth)/(login)/verify-otp/page.tsx
+++ b/src/app/(auth)/(login)/verify-otp/page.tsx
@@ -4,7 +4,6 @@
import AuthButton from "@/components/auth/AuthButton";
import AuthHead from "@/components/auth/AuthHead";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
import Link from "next/link";
import React, { useMemo, useState } from "react";
import AuthOtpInput from "@/components/auth/AuthOtpInput";
@@ -24,7 +23,6 @@ import LocalePageShell from "@/components/i18n/LocalePageShell";
function VerifyOtp() {
const { t } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const [otpExpired, setOtpExpired] = useState(false);
const [resendLoading, setResendLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
@@ -145,7 +143,6 @@ function VerifyOtp() {
{t("auth.editMobile")}
-
diff --git a/src/app/(auth)/(register)/register-otp/page.tsx b/src/app/(auth)/(register)/register-otp/page.tsx
index c953ec2..72497d0 100644
--- a/src/app/(auth)/(register)/register-otp/page.tsx
+++ b/src/app/(auth)/(register)/register-otp/page.tsx
@@ -5,7 +5,6 @@
import AuthButton from "@/components/auth/AuthButton";
import AuthHead from "@/components/auth/AuthHead";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
import Link from "next/link";
import React, { useMemo, useState } from "react";
import AuthOtpInput from "@/components/auth/AuthOtpInput";
@@ -22,7 +21,6 @@ import LocalePageShell from "@/components/i18n/LocalePageShell";
function RegisterOtp() {
const { t } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const [otpExpired, setOtpExpired] = useState(false);
const [resendLoading, setResendLoading] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
@@ -132,7 +130,6 @@ function RegisterOtp() {
{t("auth.editMobile")}
-
diff --git a/src/app/(auth)/(register)/register/fullname/page.tsx b/src/app/(auth)/(register)/register/fullname/page.tsx
index af0e183..f1673db 100644
--- a/src/app/(auth)/(register)/register/fullname/page.tsx
+++ b/src/app/(auth)/(register)/register/fullname/page.tsx
@@ -7,7 +7,7 @@ import AuthPageLayout, {
AuthFormFooter,
AuthPageContent,
} from "@/components/auth/AuthPageLayout";
-import React, { useMemo, useState } from "react";
+import React, { useEffect, useMemo, useState } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
@@ -17,6 +17,15 @@ import toast from "react-hot-toast";
import { getSafeRedirectPath } from "@/lib/auth/postLogin";
import { useTranslation } from "react-i18next";
import LocalePageShell from "@/components/i18n/LocalePageShell";
+import Image from "next/image";
+import { IMAGE_BASE_URL } from "@/components/main/BaseUrl";
+
+function splitInstagramFullName(fullName: string): { name: string; family: string } {
+ const trimmed = fullName.trim();
+ if (!trimmed) return { name: "", family: "" };
+ const [first, ...rest] = trimmed.split(/\s+/);
+ return { name: first, family: rest.join(" ") };
+}
function FullNamePage() {
const { t } = useTranslation("common");
@@ -26,6 +35,25 @@ function FullNamePage() {
const [choiceLoading, setChoiceLoading] = useState<"app" | "continue" | null>(
null
);
+ const [avatar, setAvatar] = useState(null);
+
+ const isInstagramProvider =
+ typeof window !== "undefined" &&
+ localStorage.getItem("auth_provider") === "instagram";
+ const instagramFullName =
+ isInstagramProvider ? localStorage.getItem("instagram_full_name") || "" : "";
+ const instagramSuggested = useMemo(
+ () => splitInstagramFullName(instagramFullName),
+ [instagramFullName]
+ );
+
+ useEffect(() => {
+ if (!isInstagramProvider) return;
+ request<{ user: { profile_image?: string | null } }>("GET", "/profile")
+ .then((res) => setAvatar(res?.user?.profile_image || null))
+ .catch(() => {});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
const schema = useMemo(
() =>
@@ -40,36 +68,6 @@ function FullNamePage() {
[t]
);
- const formik = useFormik({
- initialValues: {
- family: "",
- name: "",
- },
- validationSchema: schema,
- onSubmit: async (values, { setSubmitting }) => {
- try {
- await request("POST", "/register/fullname", {
- first_name: values.name.trim(),
- last_name: values.family.trim() || undefined,
- });
-
- localStorage.setItem("first_name", values.name.trim());
- if (values.family.trim()) {
- localStorage.setItem("last_name", values.family.trim());
- } else {
- localStorage.removeItem("last_name");
- }
-
- setNameSaved(true);
- toast.success(t("auth.nameSavedSuccess"));
- } catch (err: any) {
- toast.error(err?.response?.data?.message || t("auth.saveNameFailed"));
- } finally {
- setSubmitting(false);
- }
- },
- });
-
const finishRegistration = async (action: "app" | "continue") => {
setChoiceLoading(action);
try {
@@ -92,6 +90,43 @@ function FullNamePage() {
}
};
+ const formik = useFormik({
+ initialValues: {
+ family: instagramSuggested.family,
+ name: instagramSuggested.name,
+ },
+ validationSchema: schema,
+ onSubmit: async (values, { setSubmitting }) => {
+ try {
+ await request("POST", "/register/fullname", {
+ first_name: values.name.trim(),
+ last_name: values.family.trim() || undefined,
+ });
+
+ localStorage.setItem("first_name", values.name.trim());
+ if (values.family.trim()) {
+ localStorage.setItem("last_name", values.family.trim());
+ } else {
+ localStorage.removeItem("last_name");
+ }
+
+ setNameSaved(true);
+ toast.success(t("auth.nameSavedSuccess"));
+
+ // Instagram signups flow straight into the expertise/onboarding wizard —
+ // no manual "enter app or continue" choice, matching the phone/Google flow's
+ // choice screen being skipped only for this provider.
+ if (isInstagramProvider) {
+ await finishRegistration("continue");
+ }
+ } catch (err: any) {
+ toast.error(err?.response?.data?.message || t("auth.saveNameFailed"));
+ } finally {
+ setSubmitting(false);
+ }
+ },
+ });
+
return (
@@ -102,6 +137,16 @@ function FullNamePage() {
+ {isInstagramProvider && avatar && (
+
+ )}
- {formik.touched.name && formik.errors.name && (
+ {formik.touched.name && formik.errors.name ? (
{formik.errors.name}
+ ) : (
+ isInstagramProvider &&
+ instagramSuggested.name &&
+ formik.values.name === instagramSuggested.name && (
+
+ {t("auth.nameSuggestedFromInstagram")}
+
+ )
)}
- {t("auth.nameSavedChoose")}
+ {isInstagramProvider
+ ? t("auth.instagramContinuingToProfile")
+ : t("auth.nameSavedChoose")}
)}
@@ -161,7 +216,7 @@ function FullNamePage() {
>
{t("auth.saveAndContinue")}
- ) : (
+ ) : !isInstagramProvider ? (
- )}
+ ) : null}
diff --git a/src/app/(auth)/(register)/register/page.tsx b/src/app/(auth)/(register)/register/page.tsx
index 0ac2301..b0069a5 100644
--- a/src/app/(auth)/(register)/register/page.tsx
+++ b/src/app/(auth)/(register)/register/page.tsx
@@ -4,8 +4,8 @@ import AuthButton from "@/components/auth/AuthButton";
import AuthHead from "@/components/auth/AuthHead";
import AuthInput from "@/components/auth/AuthInput";
import Container from "@/components/elements/Container";
-import AuthRules from "@/components/auth/AuthRules";
-import React, { useMemo, useState } from "react";
+import AuthLanguageToggle from "@/components/auth/AuthLanguageToggle";
+import React, { useMemo } from "react";
import useAxios from "@/hooks/useAxios";
import { useFormik } from "formik";
import * as yup from "yup";
@@ -26,7 +26,6 @@ function normalizeMobileInput(value: string): string {
function Register() {
const { t, i18n } = useTranslation("common");
const router = useRouter();
- const [isModalOpen, setModalOpen] = useState(false);
const { request, loading } = useAxios();
const schema = useMemo(
@@ -86,7 +85,8 @@ function Register() {
return (
-
+
diff --git a/src/app/(auth)/verify/national-cart/page.tsx b/src/app/(auth)/verify/national-cart/page.tsx
index d7a620b..aeef786 100644
--- a/src/app/(auth)/verify/national-cart/page.tsx
+++ b/src/app/(auth)/verify/national-cart/page.tsx
@@ -13,7 +13,7 @@ import toast from "react-hot-toast";
import AuthUserDetails from "@/components/auth/AuthUserDetails";
import Image from "next/image";
import { IMAGE_BASE_URL } from "@/components/main/BaseUrl";
-import AuthRules from "@/components/auth/AuthRules";
+import Link from "next/link";
import { useTranslation } from "react-i18next";
import LocalePageShell from "@/components/i18n/LocalePageShell";
@@ -24,7 +24,6 @@ function NationalCart() {
const [nationalCartImg, setNationalCartImg] = useState
(null);
const [isCheckedOne, setIsCheckedOne] = useState(false);
- const [isModalOpen, setModalOpen] = useState(false);
const toggleCheckBox = () => setIsCheckedOne(!isCheckedOne);
@@ -144,15 +143,14 @@ function NationalCart() {
type="checkbox"
onChange={toggleCheckBox}
/>
- setModalOpen(true)}
+
{t("auth.acceptRulesLabel")}
-
+
-
-
router.push("/verify/confirm")}>
diff --git a/src/app/about-us/page.tsx b/src/app/about-us/page.tsx
index 6a19d6a..9cddb14 100644
--- a/src/app/about-us/page.tsx
+++ b/src/app/about-us/page.tsx
@@ -19,7 +19,7 @@ function AboutUs() {
{t("aboutPage.phoneLabel")}
- 09128893712
+ 02167586
diff --git a/src/app/auth/instagram/callback/page.tsx b/src/app/auth/instagram/callback/page.tsx
index dbcecd4..4454eae 100644
--- a/src/app/auth/instagram/callback/page.tsx
+++ b/src/app/auth/instagram/callback/page.tsx
@@ -16,6 +16,7 @@ import { useTranslation } from "react-i18next";
type InstagramCallbackResponse = IVerifyOtp & {
instagram_username?: string | null;
+ instagram_full_name?: string | null;
instagram_bio?: string | null;
instagram_profile_picture_url?: string | null;
};
@@ -92,6 +93,9 @@ export default function InstagramCallbackPage() {
if (raw?.instagram_username) {
localStorage.setItem("instagram_username", raw.instagram_username);
}
+ if (raw?.instagram_full_name) {
+ localStorage.setItem("instagram_full_name", raw.instagram_full_name);
+ }
if (raw?.instagram_bio) {
localStorage.setItem("instagram_bio", raw.instagram_bio);
}
diff --git a/src/app/instagram-data-deletion/page.tsx b/src/app/instagram-data-deletion/page.tsx
new file mode 100644
index 0000000..f891be9
--- /dev/null
+++ b/src/app/instagram-data-deletion/page.tsx
@@ -0,0 +1,40 @@
+"use client";
+
+import Container from "@/components/elements/Container";
+import AuthLanguageToggle from "@/components/auth/AuthLanguageToggle";
+import LocalePageShell from "@/components/i18n/LocalePageShell";
+import { useTranslation } from "react-i18next";
+import { useSearchParams } from "next/navigation";
+
+export default function InstagramDataDeletionStatusPage() {
+ const { t } = useTranslation("common");
+ const searchParams = useSearchParams();
+ const code = searchParams.get("code");
+
+ return (
+
+
+
+
+
+ {t("settings.edit.instagramImport.dataDeletionStatusTitle")}
+
+
+
+
+
+ {code
+ ? t("settings.edit.instagramImport.dataDeletionStatusDone")
+ : t("settings.edit.instagramImport.dataDeletionInstructions")}
+
+
+ {code && (
+
+ {t("settings.edit.instagramImport.dataDeletionStatusCode")}: {code}
+
+ )}
+
+
+
+ );
+}
diff --git a/src/app/offer/[id]/page.tsx b/src/app/offer/[id]/page.tsx
index 84d6055..5fc1f2d 100644
--- a/src/app/offer/[id]/page.tsx
+++ b/src/app/offer/[id]/page.tsx
@@ -7,12 +7,12 @@ import RoundedDiv from "@/components/elements/RoundedDiv";
import { selectionCardClass } from "@/lib/ui/buttonStyles";
import { BASE_URL } from "@/components/main/BaseUrl";
import UserInfo from "@/components/main/UserInfo";
+import ScoreRateStats from "@/components/main/ScoreRateStats";
import PageTitle from "@/components/settings/PageTitle";
import useAxios from "@/hooks/useAxios";
import { IOfferType, User } from "@/types/types";
import axios from "axios";
import { useFormik } from "formik";
-import Image from "next/image";
import { useRouter } from "next/navigation";
import React, { useState, useEffect, useMemo } from "react";
import toast from "react-hot-toast";
@@ -209,27 +209,10 @@ function TicketChat({ params }: ITicketChatProps) {
last_name={userDetail?.last_name}
user_name={userDetail?.user_name}
noLink
+ trailingContent={
+
+ }
/>
-
- {userDetail?.user_score || "0"}
-
-
-
- {userDetail?.rate || "0"}
-
-
) : null;
diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx
new file mode 100644
index 0000000..6ad01a4
--- /dev/null
+++ b/src/app/privacy/page.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import Container from "@/components/elements/Container";
+import AuthLanguageToggle from "@/components/auth/AuthLanguageToggle";
+import useAxios from "@/hooks/useAxios";
+import { useTranslation } from "react-i18next";
+import LocalePageShell from "@/components/i18n/LocalePageShell";
+
+export default function PrivacyPage() {
+ const { t } = useTranslation("common");
+ const [rules, setRules] = useState("");
+ const { request, loading, error } = useAxios();
+
+ useEffect(() => {
+ request<{ value: string }>("GET", "/settings/terms-and-conditions")
+ .then((data) => setRules(data?.value || ""))
+ .catch(() => {});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return (
+
+
+
+
+
+ {loading ? (
+
{t("auth.rulesLoading")}
+ ) : error ? (
+
{t("auth.rulesError")}
+ ) : (
+
+ )}
+
+
+
+ );
+}
diff --git a/src/app/settings/edit/instagram-import/page.tsx b/src/app/settings/edit/instagram-import/page.tsx
index 0a6b7f0..657e741 100644
--- a/src/app/settings/edit/instagram-import/page.tsx
+++ b/src/app/settings/edit/instagram-import/page.tsx
@@ -5,6 +5,7 @@ import PageTitle from "@/components/settings/PageTitle";
import LocalePageShell from "@/components/i18n/LocalePageShell";
import InstagramSignInButton from "@/components/auth/InstagramSignInButton";
import useAxios from "@/hooks/useAxios";
+import Link from "next/link";
import React, { useCallback, useEffect, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
@@ -15,6 +16,7 @@ type InstagramStatus = {
instagram_username: string | null;
instagram_account_type: string | null;
connected_at: string | null;
+ instagram_sync_prefs?: { posts: boolean; auto_update: boolean };
};
export default function InstagramImportSettingsPage() {
@@ -56,6 +58,10 @@ export default function InstagramImportSettingsPage() {
};
}, []);
+ const [showModeChoice, setShowModeChoice] = useState(false);
+ const [importingAuto, setImportingAuto] = useState(false);
+ const [togglingAutoOff, setTogglingAutoOff] = useState(false);
+
const handleDisconnect = async () => {
setDisconnecting(true);
try {
@@ -69,6 +75,35 @@ export default function InstagramImportSettingsPage() {
}
};
+ const handleAutomaticImport = async () => {
+ setImportingAuto(true);
+ try {
+ await request("POST", "/auth/instagram/import", { mode: "automatic" });
+ toast.success(t("settings.edit.instagramImport.automaticStartSuccess"));
+ setShowModeChoice(false);
+ await loadStatus();
+ } catch {
+ toast.error(t("settings.edit.instagramImport.importError"));
+ } finally {
+ setImportingAuto(false);
+ }
+ };
+
+ const handleTurnOffAutoUpdate = async () => {
+ setTogglingAutoOff(true);
+ try {
+ await request("POST", "/auth/instagram/import/auto-update", {});
+ toast.success(t("settings.edit.instagramImport.autoUpdateOffSuccess"));
+ await loadStatus();
+ } catch {
+ toast.error(t("settings.edit.instagramImport.importError"));
+ } finally {
+ setTogglingAutoOff(false);
+ }
+ };
+
+ const autoUpdateOn = Boolean(status?.instagram_sync_prefs?.auto_update);
+
return (
@@ -86,6 +121,71 @@ export default function InstagramImportSettingsPage() {
@{status.instagram_username}
) : null}
+
+ {autoUpdateOn && (
+
+ {t("settings.edit.instagramImport.autoUpdateOnStatus")}
+
+ )}
+
+ {!showModeChoice ? (
+
+ ) : (
+
+
+ {t("settings.edit.instagramImport.chooseModeTitle")}
+
+
+
+ {t("settings.edit.instagramImport.manualMode")}
+
+
+ {t("settings.edit.instagramImport.manualModeHint")}
+
+
+
+
+
+ )}
+
+ {autoUpdateOn && (
+
+ )}
+