feat: first version of the interface, integrate Tailwind CSS and update routing

- Add Tailwind CSS dependencies and configure Vite to use Tailwind
- Implement routing with React Router for Landing and Dashboard pages
- Remove unused App.css file and refactor App component to utilize new structure
- Update global styles in index.css to incorporate Tailwind's utility classes
This commit is contained in:
Alexis
2026-04-23 09:49:38 +02:00
parent 4627d160e8
commit a07bd3146e
26 changed files with 1819 additions and 460 deletions
+22
View File
@@ -0,0 +1,22 @@
import {
DEFAULT_BADGE_CLASSES,
TYPE_BADGE_CLASSES,
TYPE_LABELS,
} from "../../utils/publicationTypes";
/**
* Pill-style badge that colour-codes a publication type (article, review, …).
* Falls back to the neutral palette for unknown types.
*/
export function Badge({ type }) {
const label = TYPE_LABELS[type] ?? type;
const classes = TYPE_BADGE_CLASSES[type] ?? DEFAULT_BADGE_CLASSES;
return (
<span
className={`inline-flex items-center whitespace-nowrap rounded-full px-2 py-0.5 text-[11px] font-medium tracking-wide ${classes}`}
>
{label}
</span>
);
}
+104
View File
@@ -0,0 +1,104 @@
/**
* Centralised collection of inline SVG icons used across the app. Keeping
* them here avoids pulling a full icon library for ~10 glyphs while still
* letting consumers style them via `className` (stroke inherits from
* `currentColor`).
*/
const base = {
width: 16,
height: 16,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: 1.8,
strokeLinecap: "round",
strokeLinejoin: "round",
"aria-hidden": true,
};
export function DocumentIcon({ size = 36, className = "" }) {
return (
<svg {...base} width={size} height={size} className={className}>
<path d="M9 12h6M9 16h6M9 8h2" />
<path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z" />
<path d="M13 2v5a2 2 0 002 2h5" />
</svg>
);
}
export function LayersIcon({ size = 18, className = "" }) {
return (
<svg {...base} width={size} height={size} className={className}>
<path d="M12 3L2 8l10 5 10-5-10-5zM2 13l10 5 10-5M2 18l10 5 10-5" />
</svg>
);
}
export function ArrowLeftIcon({ size = 14, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={2} className={className}>
<path d="M19 12H5M12 5l-7 7 7 7" />
</svg>
);
}
export function ClockIcon({ size = 13, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={1.5} className={className}>
<circle cx="12" cy="12" r="9" />
<path d="M12 7v5l3 3" />
</svg>
);
}
export function CheckIcon({ size = 15, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={2} className={className}>
<path d="M5 13l4 4L19 7" />
</svg>
);
}
export function RefreshIcon({ size = 15, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={2} className={className}>
<path d="M1 4v6h6M23 20v-6h-6" />
<path d="M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15" />
</svg>
);
}
export function DownloadIcon({ size = 15, className = "" }) {
return (
<svg {...base} width={size} height={size} className={className}>
<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3" />
</svg>
);
}
export function ChevronDownIcon({ size = 12, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={2} className={className}>
<path d="M6 9l6 6 6-6" />
</svg>
);
}
export function SearchIcon({ size = 14, className = "" }) {
return (
<svg {...base} width={size} height={size} strokeWidth={2} className={className}>
<circle cx="11" cy="11" r="8" />
<path d="M21 21l-4.35-4.35" />
</svg>
);
}
export function AlertIcon({ size = 16, className = "" }) {
return (
<svg {...base} width={size} height={size} className={className}>
<path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z" />
<path d="M12 9v4M12 17h.01" />
</svg>
);
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Official ORCID iD glyph.
*/
export function OrcidLogo({ size = 18, className = "" }) {
return (
<svg viewBox="0 0 256 256" width={size} height={size} className={className} xmlns="http://www.w3.org/2000/svg" aria-label="ORCID iD" role="img">
<path d="M256,128c0,70.7-57.3,128-128,128C57.3,256,0,198.7,0,128C0,57.3,57.3,0,128,0C198.7,0,256,57.3,256,128z" fill="#a6ce39"/>
<path d="M86.3,186.2H70.9V79.1h15.4v48.4V186.2z" fill="#fff"/>
<path d="M108.9,79.1h41.6c39.6,0,57,28.3,57,53.6c0,27.5-21.5,53.6-56.8,53.6h-41.8V79.1z M124.3,172.4h24.5c34.9,0,42.9-26.5,42.9-39.7c0-21.5-13.7-39.7-43.7-39.7h-23.7V172.4z" fill="#fff"/>
<path d="M88.7,56.8c0,5.5-4.5,10.1-10.1,10.1c-5.6,0-10.1-4.6-10.1-10.1c0-5.6,4.5-10.1,10.1-10.1C84.2,46.7,88.7,51.3,88.7,56.8z" fill="#fff"/>
</svg>
);
}
+26
View File
@@ -0,0 +1,26 @@
/**
* Small inline spinner. Uses Tailwind's `animate-spin` utility, so no custom
* keyframes are required. Inherits colour from its parent via `currentColor`.
*/
export function Spinner({ size = 16, className = "" }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
className={`animate-spin ${className}`}
aria-hidden="true"
>
<circle
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="3"
strokeDasharray="40 20"
strokeLinecap="round"
/>
</svg>
);
}