fix(lint): clear lib/config baseline and make CI lint blocking.
Lazy-init theme from localStorage, hoist checkAuth with useCallback, named config exports for PostCSS/Tailwind, and remove the non-blocking || true wrapper from ci.yml (requires #61 + #62 merged first). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
00193651aa
commit
a530029922
5 changed files with 64 additions and 59 deletions
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
|
|
@ -36,19 +36,7 @@ jobs:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: npm
|
cache: npm
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
# TODO(fix-lint-baseline): drop the `|| true` wrapper once .convoys/fix-lint-baseline
|
- run: npm run lint --if-present
|
||||||
# lands. The codebase has ~100 pre-existing ESLint errors (conditional React
|
|
||||||
# hooks, unescaped entities, etc.). For now lint runs and posts output as a
|
|
||||||
# warning annotation so the PR check stays green while the debt is visible.
|
|
||||||
- name: Lint (non-blocking until fix-lint-baseline)
|
|
||||||
run: |
|
|
||||||
set +e
|
|
||||||
npm run lint --if-present
|
|
||||||
status=$?
|
|
||||||
if [ "$status" -ne 0 ]; then
|
|
||||||
echo "::warning title=Lint errors (non-blocking)::ESLint reported errors above. Tracked in .convoys/ship-readiness.md as P1 #11.5 (fix-lint-baseline). Remove the wrapper in .github/workflows/ci.yml after baseline is fixed."
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
schema-map-fresh:
|
schema-map-fresh:
|
||||||
name: Schema map up to date
|
name: Schema map up to date
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,17 @@ import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
const ThemeContext = createContext();
|
const ThemeContext = createContext();
|
||||||
|
|
||||||
|
function readThemeFromStorage() {
|
||||||
|
if (typeof window === 'undefined') return 'light';
|
||||||
|
return localStorage.getItem('theme') || 'light';
|
||||||
|
}
|
||||||
|
|
||||||
export function ThemeProvider({ children }) {
|
export function ThemeProvider({ children }) {
|
||||||
const [theme, setTheme] = useState('light');
|
const [theme, setTheme] = useState(readThemeFromStorage);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Check for saved theme preference or default to light
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
}, [theme]);
|
||||||
setTheme(savedTheme);
|
|
||||||
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = () => {
|
||||||
const newTheme = theme === 'light' ? 'dark' : 'light';
|
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,56 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
|
|
||||||
|
async function verifyAuthFromStorage() {
|
||||||
|
const token = localStorage.getItem('auth_token');
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/auth/verify', { headers });
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
localStorage.removeItem('auth_token');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Auth check failed:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function useAuth() {
|
export function useAuth() {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkAuth();
|
let active = true;
|
||||||
|
|
||||||
|
void (async () => {
|
||||||
|
const verifiedUser = await verifyAuthFromStorage();
|
||||||
|
if (!active) return;
|
||||||
|
setUser(verifiedUser);
|
||||||
|
setLoading(false);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
active = false;
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const checkAuth = async () => {
|
const refreshAuth = useCallback(async () => {
|
||||||
try {
|
const verifiedUser = await verifyAuthFromStorage();
|
||||||
// Get token from localStorage
|
setUser(verifiedUser);
|
||||||
const token = localStorage.getItem('auth_token');
|
setLoading(false);
|
||||||
|
}, []);
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add authorization header if token exists
|
|
||||||
if (token) {
|
|
||||||
headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch('/api/auth/verify', { headers });
|
|
||||||
if (response.ok) {
|
|
||||||
const userData = await response.json();
|
|
||||||
setUser(userData);
|
|
||||||
} else {
|
|
||||||
setUser(null);
|
|
||||||
// Clear invalid token
|
|
||||||
if (token) {
|
|
||||||
localStorage.removeItem('auth_token');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Auth check failed:', error);
|
|
||||||
setUser(null);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
localStorage.removeItem('auth_token');
|
localStorage.removeItem('auth_token');
|
||||||
|
|
@ -50,6 +61,6 @@ export function useAuth() {
|
||||||
user,
|
user,
|
||||||
loading,
|
loading,
|
||||||
logout,
|
logout,
|
||||||
refreshAuth: checkAuth
|
refreshAuth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
export default {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
const config = {
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
|
|
@ -29,4 +29,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue