This commit is contained in:
2026-03-16 08:03:03 -04:00
parent 4306d86080
commit 3e9a193d08
19 changed files with 467 additions and 304 deletions

View File

@@ -1,6 +1,8 @@
import { useState, FormEvent, useRef } from 'react';
import { Link } from 'react-router-dom';
import { useGetDocuments, useCreateDocument } from '../generated/anthoLumeAPIV1';
import { Activity, Download, Search, Upload } from 'lucide-react';
import { Button } from '../components/Button';
interface DocumentCardProps {
doc: {
@@ -16,34 +18,6 @@ interface DocumentCardProps {
};
}
// Activity icon SVG
function ActivityIcon() {
return (
<svg className="w-20 h-20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
</svg>
);
}
// Download icon SVG
function DownloadIcon({ disabled }: { disabled?: boolean }) {
if (disabled) {
return (
<svg className="w-20 h-20 text-gray-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="21 15 16 10 8 10" />
<line x1="12" y1="3" x2="12" y2="21" />
<line x1="21" y1="15" x2="21" y2="15" opacity="0" />
</svg>
);
}
return (
<svg className="w-20 h-20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="21 15 16 10 8 10" />
<line x1="12" y1="3" x2="12" y2="21" />
</svg>
);
}
function DocumentCard({ doc }: DocumentCardProps) {
const percentage = doc.percentage || 0;
const totalTimeSeconds = doc.total_time_seconds || 0;
@@ -102,14 +76,14 @@ function DocumentCard({ doc }: DocumentCardProps) {
className="absolute flex flex-col gap-2 right-4 bottom-4 text-gray-500 dark:text-gray-400"
>
<Link to={`/activity?document=${doc.id}`}>
<ActivityIcon />
<Activity size={20} />
</Link>
{doc.filepath ? (
<Link to={`/documents/${doc.id}/file`}>
<DownloadIcon />
<Download size={20} />
</Link>
) : (
<DownloadIcon disabled />
<Download size={20} className="text-gray-400" />
)}
</div>
</div>
@@ -117,26 +91,9 @@ function DocumentCard({ doc }: DocumentCardProps) {
);
}
// Search icon SVG
function SearchIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="11" cy="11" r="8" />
<path d="M21 21l-6-6" />
</svg>
);
}
// Upload icon SVG
function UploadIcon() {
return (
<svg className="w-34 h-34" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" y1="3" x2="12" y2="15" />
</svg>
);
}
export default function DocumentsPage() {
const [search, setSearch] = useState('');
@@ -203,7 +160,7 @@ export default function DocumentsPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<SearchIcon />
<Search size={15} />
</span>
<input
type="text"
@@ -216,12 +173,7 @@ export default function DocumentsPage() {
</div>
</div>
<div className="lg:w-60">
<button
type="submit"
className="font-medium px-4 py-2 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800 rounded"
>
Search
</button>
<Button variant="secondary" type="submit">Search</Button>
</div>
</form>
</div>
@@ -265,7 +217,7 @@ export default function DocumentsPage() {
onChange={() => setUploadMode(!uploadMode)}
/>
<div
className={`absolute right-0 z-10 bottom-0 rounded p-4 bg-gray-800 dark:bg-gray-200 text-white dark:text-black w-72 text-sm flex flex-col gap-2 ${uploadMode ? 'display-block' : 'display-none'}`}
className={`absolute right-0 z-10 bottom-0 rounded p-4 bg-gray-800 dark:bg-gray-200 text-white dark:text-black w-72 text-sm flex flex-col gap-2 transition-opacity duration-200 ${uploadMode ? 'visible opacity-100' : 'invisible opacity-0'}`}
>
<form
method="POST"
@@ -304,7 +256,7 @@ export default function DocumentsPage() {
className="w-16 h-16 bg-gray-800 dark:bg-gray-200 rounded-full flex items-center justify-center opacity-30 hover:opacity-100 transition-all duration-200 cursor-pointer"
htmlFor="upload-file-button"
>
<UploadIcon />
<Upload size={34} />
</label>
</div>
</div>

View File

@@ -259,4 +259,4 @@ export default function HomePage() {
</div>
</div>
);
}
}

View File

@@ -1,19 +1,29 @@
import { useState, FormEvent } from 'react';
import { useState, FormEvent, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext';
import { Button } from '../components/Button';
export default function LoginPage() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const { login } = useAuth();
const { login, isAuthenticated, isCheckingAuth } = useAuth();
const navigate = useNavigate();
// Redirect to home if already logged in
useEffect(() => {
if (!isCheckingAuth && isAuthenticated) {
navigate('/', { replace: true });
}
}, [isAuthenticated, isCheckingAuth, navigate]);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError('');
try {
await login(username, password);
} catch (err) {
@@ -59,13 +69,14 @@ export default function LoginPage() {
<span className="absolute -bottom-5 text-red-400 text-xs">{error}</span>
</div>
</div>
<button
type="submit"
<Button
variant="secondary"
type="submit"
disabled={isLoading}
className="w-full px-4 py-2 text-base font-semibold text-center text-white transition duration-200 ease-in bg-black shadow-md hover:text-black hover:bg-white focus:outline-none focus:ring-2 disabled:opacity-50"
className="w-full px-4 py-2 text-base font-semibold text-center transition duration-200 ease-in focus:outline-none focus:ring-2 disabled:opacity-50"
>
{isLoading ? 'Logging in...' : 'Login'}
</button>
</Button>
</form>
<div className="pt-12 pb-12 text-center">
<p className="mt-4">

View File

@@ -1,39 +1,8 @@
import { useState, FormEvent } from 'react';
import { useGetSearch } from '../generated/anthoLumeAPIV1';
import { GetSearchSource } from '../generated/model/getSearchSource';
// Search icon SVG
function SearchIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="11" cy="11" r="8" />
<path d="M21 21l-6-6" />
</svg>
);
}
// Documents icon SVG
function DocumentsIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
<line x1="16" y1="13" x2="8" y2="13" />
<line x1="16" y1="17" x2="8" y2="17" />
<polyline points="10 9 9 9 8 9" />
</svg>
);
}
// Download icon SVG
function DownloadIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polyline points="21 15 16 10 8 10" />
<line x1="12" y1="3" x2="12" y2="21" />
</svg>
);
}
import { Search, Book, Download } from 'lucide-react';
import { Button } from '../components/Button';
export default function SearchPage() {
const [query, setQuery] = useState('');
@@ -60,7 +29,7 @@ export default function SearchPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<SearchIcon />
<Search size={15} />
</span>
<input
type="text"
@@ -75,7 +44,7 @@ export default function SearchPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<DocumentsIcon />
<Book size={15} />
</span>
<select
value={source}
@@ -87,12 +56,7 @@ export default function SearchPage() {
</select>
</div>
<div className="lg:w-60">
<button
type="submit"
className="font-medium px-4 py-2 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800 rounded"
>
Search
</button>
<Button variant="secondary" type="submit">Search</Button>
</div>
</form>
</div>
@@ -154,7 +118,7 @@ export default function SearchPage() {
className="hover:text-purple-600"
title="Download"
>
<DownloadIcon />
<Download size={15} />
</button>
</td>
<td className="p-3 border-b border-gray-200">

View File

@@ -1,35 +1,7 @@
import { useState, FormEvent } from 'react';
import { useGetSettings } from '../generated/anthoLumeAPIV1';
// User icon SVG
function UserIcon() {
return (
<svg className="w-60 h-60" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="8" r="4" />
<path d="M12 12c-4 0-8 3-8 8h16c0-5-4-8-8-8" />
</svg>
);
}
// Password icon SVG
function PasswordIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
);
}
// Clock icon SVG
function ClockIcon() {
return (
<svg className="w-15 h-15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="10" />
<polyline points="12 6 12 12 16 14" />
</svg>
);
}
import { User, Lock, Clock } from 'lucide-react';
import { Button } from '../components/Button';
export default function SettingsPage() {
const { data, isLoading } = useGetSettings();
@@ -60,7 +32,7 @@ export default function SettingsPage() {
<div
className="flex flex-col p-4 items-center rounded shadow-lg md:w-60 lg:w-80 bg-white dark:bg-gray-700 text-gray-500 dark:text-white"
>
<UserIcon />
<User size={60} />
<p className="text-lg">{settingsData?.user?.username}</p>
</div>
</div>
@@ -80,7 +52,7 @@ export default function SettingsPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<PasswordIcon />
<Lock size={15} />
</span>
<input
type="password"
@@ -96,7 +68,7 @@ export default function SettingsPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<PasswordIcon />
<Lock size={15} />
</span>
<input
type="password"
@@ -108,12 +80,7 @@ export default function SettingsPage() {
</div>
</div>
<div className="lg:w-60">
<button
type="submit"
className="font-medium px-4 py-2 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800 rounded"
>
Submit
</button>
<Button variant="secondary" type="submit">Submit</Button>
</div>
</form>
</div>
@@ -131,7 +98,7 @@ export default function SettingsPage() {
<span
className="inline-flex items-center px-3 border-t bg-white border-l border-b border-gray-300 text-gray-500 shadow-sm text-sm"
>
<ClockIcon />
<Clock size={15} />
</span>
<select
value={timezone}
@@ -151,12 +118,7 @@ export default function SettingsPage() {
</select>
</div>
<div className="lg:w-60">
<button
type="submit"
className="font-medium px-4 py-2 text-gray-800 bg-gray-500 dark:text-white hover:bg-gray-100 dark:hover:bg-gray-800 rounded"
>
Submit
</button>
<Button variant="secondary" type="submit">Submit</Button>
</div>
</form>
</div>