EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
StoreContext.tsx
Go to the documentation of this file.
1"use client";
2import { createContext, useContext, useState, useEffect, ReactNode } from "react";
3import { apiClient } from "@/lib/client/apiClient";
4import { defaultConfig } from "@/lib/config";
5
6export interface User {
7 id: string;
8 name: string;
9 role: 'staff' | 'employee' | 'admin';
10}
11
12export interface Store {
13 id: string;
14 name: string;
15 url?: string;
16 type?: 'management' | 'store';
17}
18
19export interface Session {
20 user: User;
21 store: Store;
22 authenticated: boolean;
23}
24
25interface StoreContextType {
26 // Legacy store name support
27 storeName: string;
28 setStoreName: (name: string) => void;
29 refreshStoreName: () => Promise<void>;
30
31 // New session support
32 session: Session | null;
33 isAuthenticated: boolean;
34 isLoading: boolean;
35 error: string | null;
36 checkSession: () => Promise<void>;
37 logout: () => Promise<void>;
38}
39
40const StoreContext = createContext<StoreContextType | undefined>(undefined);
41
42export function StoreProvider({ children }: { children: ReactNode }) {
43 // Always start with server-safe default to prevent hydration mismatch
44 const [storeName, setStoreName] = useState(() => {
45 // Fallback order: Config UI name, then app name, then default
46 return defaultConfig.values?.UserInterfaceFriendlyName ||
47 process.env.NEXT_PUBLIC_APP_NAME ||
48 "CWA Standard";
49 });
50
51 const [session, setSession] = useState<Session | null>(null);
52 const [isLoading, setIsLoading] = useState(true);
53 const [error, setError] = useState<string | null>(null);
54
55 // Load from sessionStorage after hydration to avoid mismatch
56 useEffect(() => {
57 if (typeof window !== 'undefined') {
58 const cached = sessionStorage.getItem('store_name');
59 if (cached) {
60 setStoreName(cached);
61 }
62 }
63 }, []);
64
65 // Check for existing session on mount
66 useEffect(() => {
67 checkSession();
68 }, []);
69
70 async function checkSession() {
71 setIsLoading(true);
72 try {
73 const response = await fetch('/api/auth/session');
74 if (response.ok) {
75 const data = await response.json();
76 if (data.isAuthenticated && data.session) {
77 setSession(data.session);
78
79 // Update store name if available
80 if (data.session.store?.name) {
81 setStoreName(data.session.store.name);
82 if (typeof window !== 'undefined') {
83 sessionStorage.setItem('store_name', data.session.store.name);
84 }
85 }
86 } else {
87 setSession(null);
88 }
89 } else {
90 setSession(null);
91 }
92 } catch (err) {
93 console.error('Session check failed:', err);
94 setSession(null);
95 } finally {
96 setIsLoading(false);
97 }
98 }
99
100 async function logout() {
101 try {
102 await fetch('/api/auth/logout', { method: 'POST' });
103 setSession(null);
104 if (typeof window !== 'undefined') {
105 window.location.href = '/login';
106 }
107 } catch (err) {
108 console.error('Logout failed:', err);
109 }
110 }
111
112 useEffect(() => {
113 // Legacy: Fetch store name from locations API if not authenticated
114 if (session || typeof window === 'undefined') return;
115
116 const cached = sessionStorage.getItem('store_name');
117 if (cached) return;
118
119 const fetchStoreName = async () => {
120 try {
121 const result = await apiClient.getLocations();
122 if (result.success) {
123 const apiData: any = result.data || result;
124 const locationData = (!Array.isArray(apiData) && apiData?.DATS) || (!Array.isArray(apiData) && apiData?.data?.Location) || (Array.isArray(apiData) ? apiData : null);
125 const locations = Array.isArray(locationData) ? locationData : [];
126
127 if (locations.length > 0) {
128 const firstLocation = locations[0];
129 const locationName = firstLocation.f500 || firstLocation.Name;
130 if (locationName) {
131 setStoreName(locationName);
132 sessionStorage.setItem('store_name', locationName);
133 }
134 }
135 }
136 } catch (error) {
137 console.warn('Failed to fetch store name from API:', error);
138 }
139 };
140
141 fetchStoreName();
142 }, [session]);
143
144 const updateStoreName = (name: string) => {
145 setStoreName(name);
146 if (typeof window !== 'undefined') {
147 sessionStorage.setItem('store_name', name);
148 }
149 };
150
151 const refreshStoreName = async () => {
152 try {
153 const result = await apiClient.getLocations();
154 if (result.success) {
155 const apiData: any = result.data || result;
156 const locationData = (!Array.isArray(apiData) && apiData?.DATS) || (!Array.isArray(apiData) && apiData?.data?.Location) || (Array.isArray(apiData) ? apiData : null);
157 const locations = Array.isArray(locationData) ? locationData : [];
158
159 if (locations.length > 0) {
160 const firstLocation = locations[0];
161 const locationName = firstLocation.f500 || firstLocation.Name;
162 if (locationName) {
163 updateStoreName(locationName);
164 }
165 }
166 }
167 } catch (error) {
168 console.warn('Failed to refresh store name from API:', error);
169 }
170 };
171
172 return (
173 <StoreContext.Provider value={{
174 storeName,
175 setStoreName: updateStoreName,
176 refreshStoreName,
177 session,
178 isAuthenticated: session !== null && session.authenticated,
179 isLoading,
180 error,
181 checkSession,
182 logout
183 }}>
184 {children}
185 </StoreContext.Provider>
186 );
187}
188
189export function useStore() {
190 const context = useContext(StoreContext);
191 if (context === undefined) {
192 throw new Error("useStore must be used within a StoreProvider");
193 }
194 return context;
195}