fix mobile screen issues
This commit is contained in:
@@ -4,11 +4,17 @@ import { base44 } from '@/api/base44Client';
|
||||
/**
|
||||
* Auth context.
|
||||
*
|
||||
* The reference app resolves an identity provider before rendering. The demo
|
||||
* has no provider, so it resolves the seeded user immediately — but it keeps
|
||||
* the same context shape (`isLoadingAuth`, `isLoadingPublicSettings`,
|
||||
* `authError`, `navigateToLogin`, …) so `App.jsx`, `ProtectedRoute` and every
|
||||
* consumer stay unchanged.
|
||||
* The single source of truth for "is anyone signed in", and it is not a flag
|
||||
* this app sets. `checkUserAuth` calls `GET /me`; the server reads its own
|
||||
* session table and either returns a user or answers 401. Success means there
|
||||
* is a session, failure means there is not, and there is no third answer this
|
||||
* code can manufacture.
|
||||
*
|
||||
* The context shape is unchanged from the demo's version — `isLoadingAuth`,
|
||||
* `isLoadingPublicSettings`, `authError`, `navigateToLogin` and the rest — so
|
||||
* `App.jsx`, `ProtectedRoute` and every consumer stay as they were. What
|
||||
* changed is where the answer comes from: a real session cookie rather than a
|
||||
* client that reported the seeded user as permanently signed in.
|
||||
*/
|
||||
|
||||
const AuthContext = createContext(/** @type {any} */ (null));
|
||||
@@ -27,6 +33,10 @@ export const AuthProvider = ({ children }) => {
|
||||
setUser(currentUser);
|
||||
setIsAuthenticated(true);
|
||||
} catch {
|
||||
// A 401 is the ordinary state of a signed-out visitor, and it is also
|
||||
// what an expired session looks like on the first request after it
|
||||
// lapses. Both mean the same thing here.
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
} finally {
|
||||
setIsLoadingAuth(false);
|
||||
@@ -38,10 +48,27 @@ export const AuthProvider = ({ children }) => {
|
||||
checkUserAuth();
|
||||
}, []);
|
||||
|
||||
const logout = (shouldRedirect = true) => {
|
||||
/**
|
||||
* Signs in.
|
||||
*
|
||||
* Resolves to the user on success and throws the API's error otherwise. The
|
||||
* error's message is already the generic one the server chose — it does not
|
||||
* say whether the address exists — so callers show it as-is rather than
|
||||
* writing their own.
|
||||
*/
|
||||
const login = async ({ email, password, rememberMe = false }) => {
|
||||
const currentUser = await base44.auth.login({ email, password, rememberMe });
|
||||
setUser(currentUser);
|
||||
setIsAuthenticated(true);
|
||||
setAuthChecked(true);
|
||||
return currentUser;
|
||||
};
|
||||
|
||||
const logout = async (redirectTo = '/admin/login') => {
|
||||
setUser(null);
|
||||
setIsAuthenticated(false);
|
||||
base44.auth.logout(shouldRedirect ? '/' : undefined);
|
||||
// Revokes the session server-side, expires the cookie, then redirects.
|
||||
await base44.auth.logout(redirectTo);
|
||||
};
|
||||
|
||||
const navigateToLogin = () => {
|
||||
@@ -59,6 +86,7 @@ export const AuthProvider = ({ children }) => {
|
||||
authError: null,
|
||||
appPublicSettings: null,
|
||||
authChecked,
|
||||
login,
|
||||
logout,
|
||||
navigateToLogin,
|
||||
checkUserAuth,
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
/**
|
||||
* Admin sign-in state.
|
||||
*
|
||||
* The demo's data client (`base44.auth`) reports the seeded user as permanently
|
||||
* signed in — there is no identity provider behind it. That is fine for the
|
||||
* Employer and Talent demos, but the Admin console is supposed to be reached
|
||||
* through its own sign-in, and "always authenticated" would mean /admin opened
|
||||
* straight into the Control Center with the login page never seen.
|
||||
* This file used to BE the Admin session: a `sessionStorage` flag, set by the
|
||||
* login page and read by the route guard, standing in for a real one. The
|
||||
* comment on it said so plainly — "it is not security — a browser flag never
|
||||
* is — it is the state a real session would occupy … so swapping in a real
|
||||
* provider later means changing this file and nothing else."
|
||||
*
|
||||
* So Admin keeps a session marker of its own. It is deliberately thin: a single
|
||||
* flag, set by the login page and read by the route guard. It is not security —
|
||||
* a browser flag never is — it is the state a real session would occupy, put in
|
||||
* the one place that both the guard and the login screen can agree on, so the
|
||||
* flow is right now and swapping in a real provider later means changing this
|
||||
* file and nothing else.
|
||||
* That swap has happened. The session is now a row in PostgreSQL, addressed by
|
||||
* an HttpOnly cookie this code cannot read, and the question "is anyone signed
|
||||
* in" is answered by `GET /me` through `AuthContext` — see `AdminRoute`, which
|
||||
* now asks the context instead of asking here.
|
||||
*
|
||||
* `sessionStorage`, not `localStorage`: closing the tab should end the session,
|
||||
* which is both the more defensible default for an administrative console and
|
||||
* what makes the login page the app's actual starting point.
|
||||
* The three functions survive because `AdminLayout` and the Admin `Profile`
|
||||
* page call `endAdminSession()` on their sign-out buttons, and those files are
|
||||
* outside this phase. They are honest about doing nothing that matters: there
|
||||
* is no local flag left that could disagree with the server, which is the point
|
||||
* — a browser value that grants access is exactly the thing being removed.
|
||||
*/
|
||||
|
||||
const SESSION_KEY = 'krow_admin_session';
|
||||
|
||||
/** True when this tab has signed in to the Admin console. */
|
||||
/**
|
||||
* Whether this tab has signed in to the Admin console.
|
||||
*
|
||||
* @deprecated The server decides. Use `useAuth().isAuthenticated`, which
|
||||
* reflects `GET /me`, rather than anything stored in the browser.
|
||||
* @returns {boolean} always false — no local flag can grant admin access.
|
||||
*/
|
||||
export function hasAdminSession() {
|
||||
try {
|
||||
return sessionStorage.getItem(SESSION_KEY) === '1';
|
||||
} catch {
|
||||
// Private mode with storage denied: fail closed, so the login page shows.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Records a successful sign-in. */
|
||||
export function startAdminSession() {
|
||||
try {
|
||||
sessionStorage.setItem(SESSION_KEY, '1');
|
||||
} catch {
|
||||
// Nothing to do — the guard will simply ask for sign-in again.
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Records a successful sign-in.
|
||||
*
|
||||
* @deprecated A no-op. Signing in is `POST /api/v1/auth/login`, and what it
|
||||
* produces is an HttpOnly cookie, not a value in this module.
|
||||
*/
|
||||
export function startAdminSession() {}
|
||||
|
||||
/** Clears the session. Used by sign-out. */
|
||||
export function endAdminSession() {
|
||||
try {
|
||||
sessionStorage.removeItem(SESSION_KEY);
|
||||
} catch {
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Clears the session.
|
||||
*
|
||||
* @deprecated A no-op, kept because the sign-out buttons in `AdminLayout` and
|
||||
* the Admin `Profile` page still call it before `base44.auth.logout()`. The
|
||||
* logout call is what ends the session: it revokes the row server-side and
|
||||
* expires the cookie.
|
||||
*/
|
||||
export function endAdminSession() {}
|
||||
|
||||
@@ -121,7 +121,10 @@ export function toPositionPayload(draft = {}, { status = 'active' } = {}) {
|
||||
engine compares it against assignment windows. Null is open-ended. */
|
||||
duration_months: record.duration_months === '' || record.duration_months == null
|
||||
? null
|
||||
: Number(record.duration_months),
|
||||
: (Number(record.duration_months) || null),
|
||||
start_date: record.start_date && String(record.start_date).trim() !== ''
|
||||
? String(record.start_date).trim()
|
||||
: null,
|
||||
company: String(record.company || '').trim(),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user