Getting started on Tech Tracker. Added TV Context

This commit is contained in:
2025-06-09 06:59:38 -05:00
parent 5f2d25f9dd
commit cc225fae80
28 changed files with 284 additions and 91 deletions

View File

@ -17,7 +17,7 @@
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.1.0", "@hookform/resolvers": "^5.1.1",
"@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-checkbox": "^1.3.2",
"@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-dropdown-menu": "^2.1.15",

38
pnpm-lock.yaml generated
View File

@ -9,8 +9,8 @@ importers:
.: .:
dependencies: dependencies:
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^5.1.0 specifier: ^5.1.1
version: 5.1.0(react-hook-form@7.57.0(react@19.1.0)) version: 5.1.1(react-hook-form@7.57.0(react@19.1.0))
'@radix-ui/react-avatar': '@radix-ui/react-avatar':
specifier: ^1.1.10 specifier: ^1.1.10
version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@ -274,8 +274,8 @@ packages:
'@floating-ui/utils@0.2.9': '@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
'@hookform/resolvers@5.1.0': '@hookform/resolvers@5.1.1':
resolution: {integrity: sha512-A5tY8gxqvvR0lFfropqpy/gUDOxjwT7LZCxJ8lNA9puK7nFNRl/O0egGKxzdF4JSz/pnnT1O8g76P/YMyTBEFw==} resolution: {integrity: sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==}
peerDependencies: peerDependencies:
react-hook-form: ^7.55.0 react-hook-form: ^7.55.0
@ -1723,8 +1723,8 @@ packages:
peerDependencies: peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
acorn@8.14.1: acorn@8.15.0:
resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
hasBin: true hasBin: true
@ -3697,7 +3697,7 @@ snapshots:
'@floating-ui/utils@0.2.9': {} '@floating-ui/utils@0.2.9': {}
'@hookform/resolvers@5.1.0(react-hook-form@7.57.0(react@19.1.0))': '@hookform/resolvers@5.1.1(react-hook-form@7.57.0(react@19.1.0))':
dependencies: dependencies:
'@standard-schema/utils': 0.3.0 '@standard-schema/utils': 0.3.0
react-hook-form: 7.57.0(react@19.1.0) react-hook-form: 7.57.0(react@19.1.0)
@ -5157,15 +5157,15 @@ snapshots:
'@xtuc/long@4.2.2': {} '@xtuc/long@4.2.2': {}
acorn-import-attributes@1.9.5(acorn@8.14.1): acorn-import-attributes@1.9.5(acorn@8.15.0):
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
acorn-jsx@5.3.2(acorn@8.14.1): acorn-jsx@5.3.2(acorn@8.15.0):
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
acorn@8.14.1: {} acorn@8.15.0: {}
agent-base@6.0.2: agent-base@6.0.2:
dependencies: dependencies:
@ -5789,8 +5789,8 @@ snapshots:
espree@10.3.0: espree@10.3.0:
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.14.1) acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 4.2.0 eslint-visitor-keys: 4.2.0
esquery@1.6.0: esquery@1.6.0:
@ -5997,8 +5997,8 @@ snapshots:
import-in-the-middle@1.14.0: import-in-the-middle@1.14.0:
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
acorn-import-attributes: 1.9.5(acorn@8.14.1) acorn-import-attributes: 1.9.5(acorn@8.15.0)
cjs-module-lexer: 1.4.3 cjs-module-lexer: 1.4.3
module-details-from-path: 1.0.4 module-details-from-path: 1.0.4
@ -6908,7 +6908,7 @@ snapshots:
terser@5.41.0: terser@5.41.0:
dependencies: dependencies:
'@jridgewell/source-map': 0.3.6 '@jridgewell/source-map': 0.3.6
acorn: 8.14.1 acorn: 8.15.0
commander: 2.20.3 commander: 2.20.3
source-map-support: 0.5.21 source-map-support: 0.5.21
@ -7000,7 +7000,7 @@ snapshots:
unplugin@1.0.1: unplugin@1.0.1:
dependencies: dependencies:
acorn: 8.14.1 acorn: 8.15.0
chokidar: 3.6.0 chokidar: 3.6.0
webpack-sources: 3.3.2 webpack-sources: 3.3.2
webpack-virtual-modules: 0.5.0 webpack-virtual-modules: 0.5.0
@ -7077,7 +7077,7 @@ snapshots:
'@webassemblyjs/ast': 1.14.1 '@webassemblyjs/ast': 1.14.1
'@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1
'@webassemblyjs/wasm-parser': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.14.1 acorn: 8.15.0
browserslist: 4.25.0 browserslist: 4.25.0
chrome-trace-event: 1.0.4 chrome-trace-event: 1.0.4
enhanced-resolve: 5.18.1 enhanced-resolve: 5.18.1

63
public/icons/tv/enter.svg Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg513"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="Fullscreen.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview515"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="8.4359982"
inkscape:cx="69.108597"
inkscape:cy="37.458519"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="3832"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs510" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#b3b3b3;stroke-width:0.193912"
d="M 5.8615386,0 H 0 V 5.8615386 H 0.97692256 V 0.97692327 H 5.8615386 Z"
id="path396-6"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193912"
d="M 0,6.8384619 V 12.7 H 5.8615386 V 11.723076 H 0.97692256 V 6.8384619 Z"
id="path396-52"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193912"
d="M 6.8384613,12.7 H 12.7 V 6.8384619 H 11.723078 V 11.723076 H 6.8384613 Z"
id="path396-4"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193912"
d="M 12.7,5.8615386 V 0 H 6.8384613 V 0.97692327 H 11.723078 V 5.8615386 Z"
id="path396-0"
sodipodi:nodetypes="ccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

63
public/icons/tv/exit.svg Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="48"
height="48"
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg513"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="ExitFullscreen.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview515"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="8.4359982"
inkscape:cx="69.108597"
inkscape:cy="37.458519"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="3832"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs510" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#b3b3b3;stroke-width:0.193906"
d="M 0,5.8620045 H 5.8615381 L 5.8621526,0 H 4.8849607 L 4.8846152,4.8851478 H 0 Z"
id="path190"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193906"
d="M 6.8384615,8.6556325e-4 V 5.8620045 H 12.7 V 4.8851478 H 7.815384 V 8.6556325e-4 Z"
id="path396"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193906"
d="M 12.7,6.8388612 H 6.8384615 V 12.7 H 7.815384 V 7.8157173 H 12.7 Z"
id="path396-5"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#b3b3b3;stroke-width:0.193906"
d="M 5.8615381,12.7 V 6.8388612 H 0 V 7.8157173 H 4.8846152 V 12.7 Z"
id="path396-1"
sodipodi:nodetypes="ccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -35,7 +35,7 @@ export const GET = async (request: NextRequest) => {
if (type === 'invite') return redirect('/sign-up'); if (type === 'invite') return redirect('/sign-up');
} }
return redirect( return redirect(
`/?error=${encodeURIComponent(error?.message || 'Unknown error')}`, `/?error=${encodeURIComponent(error?.message ?? 'Unknown error')}`,
); );
} }

View File

@ -1,12 +1,12 @@
'use client'; 'use client';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Loader2 } from 'lucide-react'; import { Loader2 } from 'lucide-react';
const AuthSuccessPage = () => { const AuthSuccessPage = () => {
const { refreshUserData, isAuthenticated } = useAuth(); const { refreshUserData } = useAuth();
const router = useRouter(); const router = useRouter();
useEffect(() => { useEffect(() => {

View File

@ -19,7 +19,7 @@ import {
import Link from 'next/link'; import Link from 'next/link';
import { forgotPassword } from '@/lib/actions'; import { forgotPassword } from '@/lib/actions';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { StatusMessage, SubmitButton } from '@/components/default'; import { StatusMessage, SubmitButton } from '@/components/default';

View File

@ -1,5 +1,5 @@
'use client'; 'use client';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { import {

View File

@ -20,7 +20,7 @@ import {
import Link from 'next/link'; import Link from 'next/link';
import { signIn } from '@/lib/actions'; import { signIn } from '@/lib/actions';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { StatusMessage, SubmitButton } from '@/components/default'; import { StatusMessage, SubmitButton } from '@/components/default';
import { Separator } from '@/components/ui'; import { Separator } from '@/components/ui';

View File

@ -7,7 +7,7 @@ import Link from 'next/link';
import { signUp } from '@/lib/actions'; import { signUp } from '@/lib/actions';
import { StatusMessage, SubmitButton } from '@/components/default'; import { StatusMessage, SubmitButton } from '@/components/default';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { import {
Card, Card,
CardContent, CardContent,
@ -26,7 +26,7 @@ import {
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import {
SignInWithApple, SignInWithApple,
SignInWithMicrosoft SignInWithMicrosoft,
} from '@/components/default/auth'; } from '@/components/default/auth';
const formSchema = z const formSchema = z
@ -201,8 +201,8 @@ const SignUp = () => {
<span className='text-sm text-muted-foreground'>or</span> <span className='text-sm text-muted-foreground'>or</span>
<Separator className='flex-1 bg-accent py-0.5' /> <Separator className='flex-1 bg-accent py-0.5' />
</div> </div>
<SignInWithMicrosoft type='signUp' /> <SignInWithMicrosoft />
<SignInWithApple type='signUp' /> <SignInWithApple />
</CardContent> </CardContent>
</Card> </Card>
); );

View File

@ -2,8 +2,7 @@
import '@/styles/globals.css'; import '@/styles/globals.css';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { ThemeProvider } from '@/components/context/theme'; import { AuthProvider, ThemeProvider } from '@/components/context';
import { AuthProvider } from '@/components/context/auth';
import Navigation from '@/components/default/navigation'; import Navigation from '@/components/default/navigation';
import Footer from '@/components/default/footer'; import Footer from '@/components/default/footer';
import { Button, Toaster } from '@/components/ui'; import { Button, Toaster } from '@/components/ui';
@ -60,21 +59,6 @@ const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
</body> </body>
</html> </html>
); );
return (
<html lang='en'>
<body>
{/* `NextError` is the default Next.js error page component. Its type
definition requires a `statusCode` prop. However, since the App Router
does not expose status codes for errors, we simply pass 0 to render a
generic error message. */}
<NextError statusCode={0} />
{reset !== undefined && (
<Button onClick={() => reset()}>Try again</Button>
)}
</body>
</html>
);
}; };
export default GlobalError; export default GlobalError;

View File

@ -2,8 +2,11 @@ import type { Metadata } from 'next';
import '@/styles/globals.css'; import '@/styles/globals.css';
import { Geist } from 'next/font/google'; import { Geist } from 'next/font/google';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { ThemeProvider } from '@/components/context/theme'; import {
import { AuthProvider } from '@/components/context/auth'; AuthProvider,
ThemeProvider,
TVModeProvider,
} from '@/components/context';
import Navigation from '@/components/default/navigation'; import Navigation from '@/components/default/navigation';
import Footer from '@/components/default/footer'; import Footer from '@/components/default/footer';
import { Toaster } from '@/components/ui'; import { Toaster } from '@/components/ui';
@ -357,19 +360,21 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => {
disableTransitionOnChange disableTransitionOnChange
> >
<AuthProvider> <AuthProvider>
<main className='min-h-screen flex flex-col items-center'> <TVModeProvider>
<div className='flex-1 w-full flex flex-col gap-20 items-center'> <main className='min-h-screen flex flex-col items-center'>
<Navigation /> <div className='flex-1 w-full flex flex-col gap-20 items-center'>
<div <Navigation />
className='flex flex-col gap-20 max-w-5xl <div
p-5 w-full items-center' className='flex flex-col gap-20 max-w-5xl
> p-5 w-full items-center'
{children} >
{children}
</div>
</div> </div>
</div> <Footer />
<Footer /> </main>
</main> <Toaster />
<Toaster /> </TVModeProvider>
</AuthProvider> </AuthProvider>
</ThemeProvider> </ThemeProvider>
</body> </body>

View File

@ -16,7 +16,7 @@ import {
import { import {
SignInSignUp, SignInSignUp,
SignInWithApple, SignInWithApple,
SignInWithMicrosoft SignInWithMicrosoft,
} from '@/components/default/auth'; } from '@/components/default/auth';
const HomePage = async () => { const HomePage = async () => {

View File

@ -110,7 +110,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
const { const {
data: { subscription }, data: { subscription },
} = supabase.auth.onAuthStateChange(async (event, session) => { } = supabase.auth.onAuthStateChange(async (event, _session) => {
console.log('Auth state change:', event); // Debug log console.log('Auth state change:', event); // Debug log
if (event === 'SIGNED_IN') { if (event === 'SIGNED_IN') {

View File

@ -0,0 +1,79 @@
'use client';
import Image from 'next/image';
import React, { createContext, useContext, useState } from 'react';
import type { ReactNode } from 'react';
import { useAuth } from '@/components/context';
import { Button, type buttonVariants } from '@/components/ui';
import { type ComponentProps } from 'react';
import { type VariantProps } from 'class-variance-authority';
type TVModeContextProps = {
tvMode: boolean;
toggleTVMode: () => void;
};
type TVToggleProps = {
className?: ComponentProps<'button'>['className'];
buttonSize?: VariantProps<typeof buttonVariants>['size'];
buttonVariant?: VariantProps<typeof buttonVariants>['variant'];
imageWidth?: number;
imageHeight?: number;
};
const TVModeContext = createContext<TVModeContextProps | undefined>(undefined);
export const TVModeProvider = ({ children }: { children: ReactNode }) => {
const [tvMode, setTVMode] = useState(false);
const toggleTVMode = () => {
setTVMode((prev) => !prev);
};
return (
<TVModeContext.Provider value={{ tvMode, toggleTVMode }}>
{children}
</TVModeContext.Provider>
);
};
export const useTVMode = () => {
const context = useContext(TVModeContext);
if (!context) {
throw new Error('useTVMode must be used within a TVModeProvider');
}
return context;
};
export const TVToggle = ({
className = 'my-auto cursor-pointer',
buttonSize = 'default',
buttonVariant = 'link',
imageWidth = 25,
imageHeight = 25,
}: TVToggleProps) => {
const { tvMode, toggleTVMode } = useTVMode();
const { isAuthenticated } = useAuth();
if (!isAuthenticated) return <div />;
return (
<Button
onClick={toggleTVMode}
className={className}
size={buttonSize}
variant={buttonVariant}
>
{tvMode ? (
<Image
src='/icons/tv/exit.svg'
alt='Exit TV Mode'
width={imageWidth}
height={imageHeight}
/>
) : (
<Image
src='/icons/tv/enter.svg'
alt='Exit TV Mode'
width={imageWidth}
height={imageHeight}
/>
)}
</Button>
);
};

View File

@ -20,10 +20,9 @@ export const ThemeProvider = ({
return <NextThemesProvider {...props}>{children}</NextThemesProvider>; return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}; };
export interface ThemeToggleProps type ThemeToggleProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: number; size?: number;
} };
export const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => { export const ThemeToggle = ({ size = 1, ...props }: ThemeToggleProps) => {
const { setTheme, resolvedTheme } = useTheme(); const { setTheme, resolvedTheme } = useTheme();

View File

@ -0,0 +1,3 @@
export { AuthProvider, useAuth } from './Auth';
export { ThemeProvider, ThemeToggle } from './Theme';
export { TVModeProvider, useTVMode, TVToggle } from './TVMode';

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { signInWithApple } from '@/lib/actions'; import { signInWithApple } from '@/lib/actions';
import { StatusMessage, SubmitButton } from '@/components/default'; import { StatusMessage, SubmitButton } from '@/components/default';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
import Image from 'next/image'; import Image from 'next/image';
@ -37,7 +37,7 @@ export const SignInWithApple = ({
// Redirect to Apple OAuth page // Redirect to Apple OAuth page
window.location.href = result.data; window.location.href = result.data;
} else { } else {
setStatusMessage(`Error: ${result.error}`); setStatusMessage(`Error signing in with Apple!`);
} }
} catch (error) { } catch (error) {
setStatusMessage( setStatusMessage(

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { signInWithMicrosoft } from '@/lib/actions'; import { signInWithMicrosoft } from '@/lib/actions';
import { StatusMessage, SubmitButton } from '@/components/default'; import { StatusMessage, SubmitButton } from '@/components/default';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useState } from 'react'; import { useState } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import { type buttonVariants } from '@/components/ui'; import { type buttonVariants } from '@/components/ui';
@ -35,7 +35,7 @@ export const SignInWithMicrosoft = ({
// Redirect to Microsoft OAuth page // Redirect to Microsoft OAuth page
window.location.href = result.data; window.location.href = result.data;
} else { } else {
setStatusMessage(`Error: ${result.error}`); setStatusMessage(`Error: Could not sign in with Microsoft!`);
} }
} catch (error) { } catch (error) {
setStatusMessage( setStatusMessage(

View File

@ -12,7 +12,7 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui'; } from '@/components/ui';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { signOut } from '@/lib/actions'; import { signOut } from '@/lib/actions';
import { User } from 'lucide-react'; import { User } from 'lucide-react';

View File

@ -3,7 +3,7 @@
import Link from 'next/link'; import Link from 'next/link';
import { Button } from '@/components/ui'; import { Button } from '@/components/ui';
import NavigationAuth from './auth'; import NavigationAuth from './auth';
import { ThemeToggle } from '@/components/context/theme'; import { ThemeToggle, TVToggle } from '@/components/context';
import Image from 'next/image'; import Image from 'next/image';
const Navigation = () => { const Navigation = () => {
@ -30,6 +30,7 @@ const Navigation = () => {
</div> </div>
</div> </div>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<TVToggle />
<ThemeToggle /> <ThemeToggle />
<NavigationAuth /> <NavigationAuth />
</div> </div>

View File

@ -1,5 +1,5 @@
import { useFileUpload } from '@/lib/hooks/useFileUpload'; import { useFileUpload } from '@/lib/hooks/useFileUpload';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { import {
Avatar, Avatar,
AvatarFallback, AvatarFallback,

View File

@ -13,7 +13,7 @@ import {
Input, Input,
} from '@/components/ui'; } from '@/components/ui';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { SubmitButton } from '@/components/default'; import { SubmitButton } from '@/components/default';
const formSchema = z.object({ const formSchema = z.object({

View File

@ -122,18 +122,15 @@ export const ResetPasswordForm = ({
</FormItem> </FormItem>
)} )}
/> />
{statusMessage && ( {statusMessage &&
<div (statusMessage.includes('Error') ||
className={`text-sm text-center ${ statusMessage.includes('error') ||
statusMessage.includes('Error') || statusMessage.includes('failed') ||
statusMessage.includes('failed') statusMessage.includes('invalid') ? (
? 'text-destructive' <StatusMessage message={{ error: statusMessage }} />
: 'text-green-600' ) : (
}`} <StatusMessage message={{ message: statusMessage }} />
> ))}
{statusMessage}
</div>
)}
<div className='flex justify-center'> <div className='flex justify-center'>
<SubmitButton <SubmitButton
disabled={isLoading} disabled={isLoading}

View File

@ -3,7 +3,7 @@
import { CardHeader } from '@/components/ui'; import { CardHeader } from '@/components/ui';
import { SubmitButton } from '@/components/default'; import { SubmitButton } from '@/components/default';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { signOut } from '@/lib/actions'; import { signOut } from '@/lib/actions';
export const SignOut = () => { export const SignOut = () => {

View File

@ -149,7 +149,7 @@ export const getUser = async (): Promise<Result<User>> => {
const { data, error } = await supabase.auth.getUser(); const { data, error } = await supabase.auth.getUser();
if (error) throw error; if (error) throw error;
return { success: true, data: data.user }; return { success: true, data: data.user };
} catch (error) { } catch {
return { success: false, error: 'Could not get user!' }; return { success: false, error: `Could not get user!` };
} }
}; };

View File

@ -1,6 +1,5 @@
export * from './auth'; export * from './auth';
export * from './public'; export * from './public';
//export * from './resizeImage';
export * from './storage'; export * from './storage';
export * from './useFileUpload'; export * from './useFileUpload';

View File

@ -3,7 +3,7 @@
import { useState, useRef } from 'react'; import { useState, useRef } from 'react';
import { replaceFile, uploadFile } from '@/lib/hooks'; import { replaceFile, uploadFile } from '@/lib/hooks';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useAuth } from '@/components/context/auth'; import { useAuth } from '@/components/context';
import { resizeImage } from '@/lib/hooks'; import { resizeImage } from '@/lib/hooks';
import type { Result } from '.'; import type { Result } from '.';