mirror of
https://github.com/torrentpier/torrentpier
synced 2025-07-16 10:03:11 -07:00
feat: initialize Laravel project with React 19 starter kit (#2001)
* feat: initialize Laravel project with React 19 starter kit - Add Laravel backend with Inertia.js integration - Set up React 19 with TypeScript frontend - Configure shadcn/ui component library - Add Vite build tooling and development setup - Include authentication scaffolding with Inertia - Set up responsive layouts and UI components * Potential fix for code scanning alert no. 595: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
parent
b8e47d569e
commit
dbbc31b519
163 changed files with 25318 additions and 24 deletions
73
resources/js/hooks/use-appearance.tsx
Normal file
73
resources/js/hooks/use-appearance.tsx
Normal file
|
@ -0,0 +1,73 @@
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export type Appearance = 'light' | 'dark' | 'system';
|
||||
|
||||
const prefersDark = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
};
|
||||
|
||||
const setCookie = (name: string, value: string, days = 365) => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const maxAge = days * 24 * 60 * 60;
|
||||
document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;
|
||||
};
|
||||
|
||||
const applyTheme = (appearance: Appearance) => {
|
||||
const isDark = appearance === 'dark' || (appearance === 'system' && prefersDark());
|
||||
|
||||
document.documentElement.classList.toggle('dark', isDark);
|
||||
};
|
||||
|
||||
const mediaQuery = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.matchMedia('(prefers-color-scheme: dark)');
|
||||
};
|
||||
|
||||
const handleSystemThemeChange = () => {
|
||||
const currentAppearance = localStorage.getItem('appearance') as Appearance;
|
||||
applyTheme(currentAppearance || 'system');
|
||||
};
|
||||
|
||||
export function initializeTheme() {
|
||||
const savedAppearance = (localStorage.getItem('appearance') as Appearance) || 'system';
|
||||
|
||||
applyTheme(savedAppearance);
|
||||
|
||||
// Add the event listener for system theme changes...
|
||||
mediaQuery()?.addEventListener('change', handleSystemThemeChange);
|
||||
}
|
||||
|
||||
export function useAppearance() {
|
||||
const [appearance, setAppearance] = useState<Appearance>('system');
|
||||
|
||||
const updateAppearance = useCallback((mode: Appearance) => {
|
||||
setAppearance(mode);
|
||||
|
||||
// Store in localStorage for client-side persistence...
|
||||
localStorage.setItem('appearance', mode);
|
||||
|
||||
// Store in cookie for SSR...
|
||||
setCookie('appearance', mode);
|
||||
|
||||
applyTheme(mode);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const savedAppearance = localStorage.getItem('appearance') as Appearance | null;
|
||||
updateAppearance(savedAppearance || 'system');
|
||||
|
||||
return () => mediaQuery()?.removeEventListener('change', handleSystemThemeChange);
|
||||
}, [updateAppearance]);
|
||||
|
||||
return { appearance, updateAppearance } as const;
|
||||
}
|
15
resources/js/hooks/use-initials.tsx
Normal file
15
resources/js/hooks/use-initials.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
export function useInitials() {
|
||||
return useCallback((fullName: string): string => {
|
||||
const names = fullName.trim().split(' ');
|
||||
|
||||
if (names.length === 0) return '';
|
||||
if (names.length === 1) return names[0].charAt(0).toUpperCase();
|
||||
|
||||
const firstInitial = names[0].charAt(0);
|
||||
const lastInitial = names[names.length - 1].charAt(0);
|
||||
|
||||
return `${firstInitial}${lastInitial}`.toUpperCase();
|
||||
}, []);
|
||||
}
|
8
resources/js/hooks/use-mobile-navigation.ts
Normal file
8
resources/js/hooks/use-mobile-navigation.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
export function useMobileNavigation() {
|
||||
return useCallback(() => {
|
||||
// Remove pointer-events style from body...
|
||||
document.body.style.removeProperty('pointer-events');
|
||||
}, []);
|
||||
}
|
22
resources/js/hooks/use-mobile.tsx
Normal file
22
resources/js/hooks/use-mobile.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = useState<boolean>();
|
||||
|
||||
useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
|
||||
mql.addEventListener('change', onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
|
||||
return () => mql.removeEventListener('change', onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue