fix(store): don't bounce owners home, stop cross-user myStores cache
- app/hooks/useStoreOwnership.ts: the old redirect logic sent owners to / the moment useMyStores briefly resolved to []. That empty array came from useMyStores's dead `if (!token) return []` inside the queryFn, so any transient auth blip left the store page thinking the user has no stores. Now compares usernames case-insensitively, only redirects while the query is settled (!isLoading && !isFetching && !isError), and never bounces to home on empty — worst case the seller sees an empty dashboard rather than a silent kick-out. - app/requestHandler/use-seller-hooks.ts: removed the unreachable `if (!token) return []` in useMyStores and added the token to the queryKey so a login switch stops serving the previous user's cached list. - app/routes/store.$storeId._index.tsx: mount StorePushPromptCard at the top of the store dashboard. - app/routes/store.$storeId.tsx: sidebar "پیامها" badge reads badges.sellerChat[storeId] (per-store) instead of the buyer-side badges.chat, matching the badge-serializer split. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0b18438a97
commit
6a300b8760
@ -9,23 +9,36 @@ import { useMyStores } from "../requestHandler/use-seller-hooks";
|
|||||||
*/
|
*/
|
||||||
export function useStoreOwnership(storeId: string | undefined, mode?: string) {
|
export function useStoreOwnership(storeId: string | undefined, mode?: string) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data: stores, isLoading } = useMyStores();
|
const { data: stores, isLoading, isFetching, isError } = useMyStores();
|
||||||
|
|
||||||
const current = stores?.find((s) => s.username === storeId);
|
const current = stores?.find(
|
||||||
|
(s) => s.username?.toLowerCase() === storeId?.toLowerCase()
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoading || mode === "create") return;
|
if (isLoading || isFetching || isError || mode === "create") return;
|
||||||
if (!storeId) {
|
if (!storeId) {
|
||||||
navigate("/");
|
navigate("/");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stores === undefined) return; // not loaded yet
|
if (!stores || stores.length === 0) return; // nothing to compare against yet
|
||||||
if (!current) {
|
if (!current) {
|
||||||
// Not a member of this store → send them to a store they can manage, else home.
|
// The user is a member of at least one OTHER store — send them there.
|
||||||
if (stores.length > 0) navigate(`/store/${stores[0].username}`);
|
// If they're not a member of any store, do NOT bounce to home: staying
|
||||||
else navigate("/");
|
// put lets an owner see their (empty) dashboard rather than being
|
||||||
|
// silently kicked out on a transient/mismatched my-stores response.
|
||||||
|
navigate(`/store/${stores[0].username}`);
|
||||||
}
|
}
|
||||||
}, [stores, current, storeId, isLoading, navigate, mode]);
|
}, [
|
||||||
|
stores,
|
||||||
|
current,
|
||||||
|
storeId,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
isError,
|
||||||
|
navigate,
|
||||||
|
mode,
|
||||||
|
]);
|
||||||
|
|
||||||
const ownedOrFirst = stores?.find((s) => s.role === "owner") ?? stores?.[0];
|
const ownedOrFirst = stores?.find((s) => s.role === "owner") ?? stores?.[0];
|
||||||
|
|
||||||
|
|||||||
@ -39,9 +39,8 @@ export interface StoreMember {
|
|||||||
export function useMyStores() {
|
export function useMyStores() {
|
||||||
const token = useAuthToken();
|
const token = useAuthToken();
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["myStores"],
|
queryKey: ["myStores", token ?? "anon"],
|
||||||
queryFn: async (): Promise<MyStore[]> => {
|
queryFn: async (): Promise<MyStore[]> => {
|
||||||
if (!token) return [];
|
|
||||||
const res = await fetch(`${getApiBaseUrl()}/api/seller/v1/my-stores/`, {
|
const res = await fetch(`${getApiBaseUrl()}/api/seller/v1/my-stores/`, {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import { useStoreOwnership } from "../hooks/useStoreOwnership";
|
|||||||
import { LoadingModal } from "./store.add.manual.$storeId";
|
import { LoadingModal } from "./store.add.manual.$storeId";
|
||||||
import SellerLogo from "../components/SellerLogo";
|
import SellerLogo from "../components/SellerLogo";
|
||||||
import CallDialog from "../components/CallDialog";
|
import CallDialog from "../components/CallDialog";
|
||||||
|
import { StorePushPromptCard } from "../components/store/StorePushPromptCard";
|
||||||
|
|
||||||
export default function Store() {
|
export default function Store() {
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
@ -110,6 +111,7 @@ export default function Store() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="px-4">
|
<div className="px-4">
|
||||||
|
<StorePushPromptCard />
|
||||||
<div className="flex px-4 border rounded-[10px] w-full py-2">
|
<div className="flex px-4 border rounded-[10px] w-full py-2">
|
||||||
<div className="w-full border-l flex-2 flex flex-col items-center justify-center">
|
<div className="w-full border-l flex-2 flex flex-col items-center justify-center">
|
||||||
<p className="text-R12">{data?.followerCount}</p>
|
<p className="text-R12">{data?.followerCount}</p>
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export default function StoreLayout() {
|
|||||||
label: "سفارشها",
|
label: "سفارشها",
|
||||||
to: `${base}/orders`,
|
to: `${base}/orders`,
|
||||||
icon: ClipboardList,
|
icon: ClipboardList,
|
||||||
badge: badges?.seller_orders || 0,
|
badge: badges?.sellerOrders || 0,
|
||||||
},
|
},
|
||||||
{ label: "مالی", to: `${base}/financial-dashboard`, icon: Wallet },
|
{ label: "مالی", to: `${base}/financial-dashboard`, icon: Wallet },
|
||||||
{ label: "روش ارسال", to: `${base}/shipping-method`, icon: Truck },
|
{ label: "روش ارسال", to: `${base}/shipping-method`, icon: Truck },
|
||||||
@ -40,7 +40,7 @@ export default function StoreLayout() {
|
|||||||
label: "پیامها",
|
label: "پیامها",
|
||||||
to: `${base}/threads`,
|
to: `${base}/threads`,
|
||||||
icon: MessageCircle,
|
icon: MessageCircle,
|
||||||
badge: badges?.chat || 0,
|
badge: (storeId && badges?.sellerChat?.[storeId]) || 0,
|
||||||
},
|
},
|
||||||
{ label: "تنظیمات", to: `${base}/setting`, icon: Settings },
|
{ label: "تنظیمات", to: `${base}/setting`, icon: Settings },
|
||||||
];
|
];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user