diff --git a/src/components/Componentes/error-toast.tsx b/src/components/Componentes/error-toast.tsx
index f23b366..b94a390 100644
--- a/src/components/Componentes/error-toast.tsx
+++ b/src/components/Componentes/error-toast.tsx
@@ -1,16 +1,18 @@
"use client";
import { useEffect, useState } from "react";
-import { IoAlertCircle, IoClose, IoCheckmarkCircle } from "react-icons/io5";
+import { IoClose } from "react-icons/io5";
type ErrorToastProps = {
+ title?: string;
message: string;
onClose: () => void;
duration?: number;
- variant?: "error" | "success";
+ variant?: "error" | "success" | "warning" | "info";
};
export default function ErrorToast({
+ title,
message,
onClose,
duration = 4000,
@@ -32,42 +34,56 @@ export default function ErrorToast({
setTimeout(onClose, 300);
};
- const isSuccess = variant === "success";
+ const getBorderColor = () => {
+ switch (variant) {
+ case "success":
+ return "border-t-[#10B981]";
+ case "warning":
+ return "border-t-[#F59E0B]";
+ case "info":
+ return "border-t-[#3B82F6]";
+ case "error":
+ default:
+ return "border-t-[#F0445B]";
+ }
+ };
return (
- {isSuccess ? (
-
- ) : (
-
- )}
-
- {message}
-
+
+ {title && (
+
+ {title}
+
+ )}
+
+ {message}
+
+
+
);
}
+
diff --git a/src/components/Componentes/question-section-flow.tsx b/src/components/Componentes/question-section-flow.tsx
index 69b5aa2..a9056d7 100644
--- a/src/components/Componentes/question-section-flow.tsx
+++ b/src/components/Componentes/question-section-flow.tsx
@@ -12,6 +12,7 @@ import QuestionProgressTracker, {
} from "./question-progress-tracker";
import QuestionSnapList from "./question-snap-list";
import type { QuestionField } from "@/lib/schema-adapter";
+import ErrorToast from "./error-toast";
import NoticeBox from "./notice-box";
import AnswerPaceSheet from "@/app/questions-list/[slug]/answer-pace-sheet";
import { FixToTheEnd } from "./fix-to-the-end";
@@ -50,6 +51,7 @@ function SectionFlowContent({
const { markQuestionPassed, isCompleted } = useQuestionProgress();
const [activeQuestionIndex, setActiveQuestionIndex] = useState(0);
const [isSubmitting, setIsSubmitting] = useState(false);
+ const [errorMessage, setErrorMessage] = useState(null);
const handleQuestionExit = useCallback(() => {
void flushAnswers({ force: true });
@@ -60,19 +62,46 @@ function SectionFlowContent({
return;
}
setIsSubmitting(true);
+ setErrorMessage(null);
- try {
+ const MAX_RETRIES = 3;
+ let success = false;
+
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
+ try {
+ await flushAnswers({ force: true });
+ success = true;
+ break;
+ } catch (err) {
+ console.warn(
+ `[CONTINUE] flushAnswers attempt ${attempt}/${MAX_RETRIES} failed:`,
+ err,
+ );
+ if (attempt < MAX_RETRIES) {
+ // Silent delay between retries while maintaining loading spinner
+ await new Promise((resolve) => setTimeout(resolve, 800));
+ }
+ }
+ }
+
+ if (success) {
markFirstEntryCompleted();
- await flushAnswers({ force: true });
- } catch {
- // ignore
- } finally {
if (onExit) {
onExit();
} else {
const target = localizePath(exitHref || "/questions-list", locale);
router.replace(target);
}
+ } else {
+ setIsSubmitting(false);
+ const isPersian = locale === "fa" || locale === "fa-ir";
+ const isArabic = locale === "ar";
+ const msg = isPersian
+ ? "خطا در اتصال به اینترنت. پاسخها با سرور همگام نشدند؛ لطفاً اتصال خود را بررسی و دوباره روی ادامه بزنید."
+ : isArabic
+ ? "خطأ في الاتصال بالإنترنت. تعذر مزامنة الإجابات مع الخادم؛ يرجى التحقق من الاتصال والمحاولة مرة أخرى."
+ : "Internet connection error. Answers could not be synced with server. Please check your connection and tap Continue again.";
+ setErrorMessage(msg);
}
}, [exitHref, onExit, flushAnswers, locale, router, isSubmitting]);
@@ -137,6 +166,14 @@ function SectionFlowContent({
{process.env.NODE_ENV === "development" ? (
) : null}
+ {errorMessage && (
+ setErrorMessage(null)}
+ duration={5000}
+ variant="error"
+ />
+ )}