Upload 2 files
Browse files- App.tsx +79 -0
- vite-env.d.ts +40 -0
App.tsx
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useState, useEffect } from 'react';
|
| 2 |
+
import type { Session } from '@supabase/supabase-js';
|
| 3 |
+
import { supabase, isSupabaseConfigured } from './services/supabaseClient';
|
| 4 |
+
import Loader from './components/Loader';
|
| 5 |
+
import AppHeader from './components/AppHeader';
|
| 6 |
+
import Statistics from './components/Statistics';
|
| 7 |
+
import EssayChecker from './components/EssayChecker';
|
| 8 |
+
import Dashboard from './components/Dashboard';
|
| 9 |
+
import TasksTab from './components/TasksTab';
|
| 10 |
+
import Auth from './components/Auth';
|
| 11 |
+
|
| 12 |
+
export type ViewState = 'checker' | 'stats' | 'dashboard' | 'tasks' | 'auth';
|
| 13 |
+
|
| 14 |
+
const App: React.FC = () => {
|
| 15 |
+
const [session, setSession] = useState<Session | null>(null);
|
| 16 |
+
const [loading, setLoading] = useState(true);
|
| 17 |
+
const [view, setView] = useState<ViewState>('checker');
|
| 18 |
+
|
| 19 |
+
const handleSignOut = async () => {
|
| 20 |
+
if (isSupabaseConfigured()) {
|
| 21 |
+
await supabase.auth.signOut();
|
| 22 |
+
}
|
| 23 |
+
setSession(null);
|
| 24 |
+
setView('checker');
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
useEffect(() => {
|
| 28 |
+
if (isSupabaseConfigured()) {
|
| 29 |
+
supabase.auth.getSession().then(({ data }) => {
|
| 30 |
+
setSession(data?.session ?? null);
|
| 31 |
+
setLoading(false);
|
| 32 |
+
});
|
| 33 |
+
|
| 34 |
+
const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
|
| 35 |
+
setSession(session);
|
| 36 |
+
});
|
| 37 |
+
|
| 38 |
+
return () => subscription.unsubscribe();
|
| 39 |
+
} else {
|
| 40 |
+
setLoading(false);
|
| 41 |
+
}
|
| 42 |
+
}, []);
|
| 43 |
+
|
| 44 |
+
if (loading) {
|
| 45 |
+
return <div className="min-h-screen flex justify-center items-center bg-refined-paper"><Loader /></div>;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
return (
|
| 49 |
+
<div className="min-h-screen bg-refined-paper text-refined-dark font-sans flex flex-col items-center relative overflow-x-hidden selection:bg-refined-red/20 selection:text-refined-dark">
|
| 50 |
+
<div className="absolute top-0 left-0 w-full h-[600px] bg-gradient-to-b from-white to-transparent opacity-60 pointer-events-none z-0"></div>
|
| 51 |
+
|
| 52 |
+
<main className="w-full max-w-7xl mx-auto z-10 relative flex-grow flex flex-col px-4 md:px-8 py-6">
|
| 53 |
+
<AppHeader session={session} view={view} setView={setView} onSignOut={handleSignOut} />
|
| 54 |
+
<div className="flex-grow fade-in-section">
|
| 55 |
+
{(() => {
|
| 56 |
+
switch (view) {
|
| 57 |
+
case 'stats':
|
| 58 |
+
return <Statistics session={session} />;
|
| 59 |
+
case 'dashboard':
|
| 60 |
+
return <Dashboard session={session} />;
|
| 61 |
+
case 'tasks':
|
| 62 |
+
return <TasksTab session={session} />;
|
| 63 |
+
case 'auth':
|
| 64 |
+
return <Auth session={session} setView={setView} />;
|
| 65 |
+
case 'checker':
|
| 66 |
+
default:
|
| 67 |
+
return <EssayChecker session={session} />;
|
| 68 |
+
}
|
| 69 |
+
})()}
|
| 70 |
+
</div>
|
| 71 |
+
</main>
|
| 72 |
+
<footer className="w-full py-6 text-center text-stone-400 text-sm z-10 print:hidden">
|
| 73 |
+
© {new Date().getFullYear()} Refined Quill. Искусство Слова.
|
| 74 |
+
</footer>
|
| 75 |
+
</div>
|
| 76 |
+
);
|
| 77 |
+
};
|
| 78 |
+
|
| 79 |
+
export default App;
|
vite-env.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Manually declare modules and interfaces usually provided by vite/client
|
| 2 |
+
// to fix "Cannot find type definition file for 'vite/client'" error.
|
| 3 |
+
|
| 4 |
+
declare module '*.svg' {
|
| 5 |
+
import * as React from 'react';
|
| 6 |
+
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement> & { title?: string }>;
|
| 7 |
+
const src: string;
|
| 8 |
+
export default src;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
declare module '*.jpg' {
|
| 12 |
+
const content: string;
|
| 13 |
+
export default content;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
declare module '*.png' {
|
| 17 |
+
const content: string;
|
| 18 |
+
export default content;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
declare module '*.json' {
|
| 22 |
+
const content: string;
|
| 23 |
+
export default content;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
declare module '*.module.css' {
|
| 27 |
+
const classes: { readonly [key: string]: string };
|
| 28 |
+
export default classes;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
interface ImportMetaEnv {
|
| 32 |
+
readonly VITE_SUPABASE_URL: string;
|
| 33 |
+
readonly VITE_SUPABASE_ANON_KEY: string;
|
| 34 |
+
readonly VITE_SCRAPER_URL: string;
|
| 35 |
+
readonly API_KEY: string;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
interface ImportMeta {
|
| 39 |
+
readonly env: ImportMetaEnv;
|
| 40 |
+
}
|