feat: implement AccessDeniedScreen and enhance error handling
- Introduced AccessDeniedScreen component for improved user experience when access is denied, replacing the previous AccessDenied component. - Updated CurrentDutyView and CalendarPage to handle access denied scenarios, displaying the new screen appropriately. - Enhanced tests for CurrentDutyView and AccessDeniedScreen to ensure correct rendering and functionality under access denied conditions. - Refactored localization messages to include new labels for access denied scenarios in both English and Russian.
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { AccessDeniedScreen } from "@/components/states/AccessDeniedScreen";
|
||||
import type { DutyWithUser } from "@/types";
|
||||
|
||||
export interface CurrentDutyViewProps {
|
||||
@@ -39,7 +40,7 @@ export interface CurrentDutyViewProps {
|
||||
openedFromPin?: boolean;
|
||||
}
|
||||
|
||||
type ViewState = "loading" | "error" | "ready";
|
||||
type ViewState = "loading" | "error" | "accessDenied" | "ready";
|
||||
|
||||
/**
|
||||
* Full-screen current duty view with Telegram BackButton and auto-updating remaining time.
|
||||
@@ -53,6 +54,7 @@ export function CurrentDutyView({ onBack, openedFromPin = false }: CurrentDutyVi
|
||||
const [state, setState] = useState<ViewState>("loading");
|
||||
const [duty, setDuty] = useState<DutyWithUser | null>(null);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [accessDeniedDetail, setAccessDeniedDetail] = useState<string | null>(null);
|
||||
const [remaining, setRemaining] = useState<{ hours: number; minutes: number } | null>(null);
|
||||
|
||||
const loadTodayDuties = useCallback(
|
||||
@@ -74,14 +76,17 @@ export function CurrentDutyView({ onBack, openedFromPin = false }: CurrentDutyVi
|
||||
}
|
||||
} catch (e) {
|
||||
if (signal?.aborted) return;
|
||||
setState("error");
|
||||
const msg =
|
||||
e instanceof AccessDeniedError && e.serverDetail
|
||||
? e.serverDetail
|
||||
: t("error_generic");
|
||||
setErrorMessage(msg);
|
||||
setDuty(null);
|
||||
setRemaining(null);
|
||||
if (e instanceof AccessDeniedError) {
|
||||
setState("accessDenied");
|
||||
setAccessDeniedDetail(e.serverDetail ?? null);
|
||||
setDuty(null);
|
||||
setRemaining(null);
|
||||
} else {
|
||||
setState("error");
|
||||
setErrorMessage(t("error_generic"));
|
||||
setDuty(null);
|
||||
setRemaining(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
[initDataRaw, lang, t]
|
||||
@@ -195,6 +200,17 @@ export function CurrentDutyView({ onBack, openedFromPin = false }: CurrentDutyVi
|
||||
);
|
||||
}
|
||||
|
||||
if (state === "accessDenied") {
|
||||
return (
|
||||
<AccessDeniedScreen
|
||||
serverDetail={accessDeniedDetail}
|
||||
primaryAction="back"
|
||||
onBack={handlePrimaryAction}
|
||||
openedFromPin={openedFromPin}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === "error") {
|
||||
const handleRetry = () => {
|
||||
setState("loading");
|
||||
|
||||
Reference in New Issue
Block a user