Upload folder using huggingface_hub
Browse files- client/src/components/HitokotoBar.tsx +6 -6
- client/src/components/Layout.tsx +11 -11
- client/src/index.css +10 -6
- client/src/pages/Dashboard.tsx +19 -19
- client/tailwind.config.js +16 -5
client/src/components/HitokotoBar.tsx
CHANGED
|
@@ -62,16 +62,16 @@ const HitokotoBar: React.FC = () => {
|
|
| 62 |
return (
|
| 63 |
<div className="fixed bottom-0 right-0 z-50 w-[calc(100%-240px)] md:w-[calc(100%-240px)]">
|
| 64 |
<div className="ml-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-3">
|
| 65 |
-
<div className="bg-
|
| 66 |
<div className="flex items-center min-w-0">
|
| 67 |
<span className="mr-2 text-ui-navy">💬</span>
|
| 68 |
<div className="min-w-0">
|
| 69 |
{loading ? (
|
| 70 |
-
<div className="text-sm text-ui-
|
| 71 |
) : error ? (
|
| 72 |
-
<div className="text-sm text-red-
|
| 73 |
) : (
|
| 74 |
-
<div className="text-sm text-ui-
|
| 75 |
{quote ? `『${quote.hitokoto}』 — ${quote.from_who || quote.from || 'Hitokoto'}` : ' '}
|
| 76 |
</div>
|
| 77 |
)}
|
|
@@ -80,14 +80,14 @@ const HitokotoBar: React.FC = () => {
|
|
| 80 |
<div className="flex items-center space-x-2 flex-shrink-0">
|
| 81 |
<button
|
| 82 |
onClick={fetchQuote}
|
| 83 |
-
className="text-xs px-2 py-1 rounded bg-ui-
|
| 84 |
title="Next quote"
|
| 85 |
>
|
| 86 |
Next
|
| 87 |
</button>
|
| 88 |
<button
|
| 89 |
onClick={() => { setHidden(true); localStorage.setItem(LOCAL_HIDE_KEY, '1'); }}
|
| 90 |
-
className="text-xs px-2 py-1 rounded bg-ui-
|
| 91 |
title="Hide"
|
| 92 |
>
|
| 93 |
Hide
|
|
|
|
| 62 |
return (
|
| 63 |
<div className="fixed bottom-0 right-0 z-50 w-[calc(100%-240px)] md:w-[calc(100%-240px)]">
|
| 64 |
<div className="ml-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-3">
|
| 65 |
+
<div className="bg-ui-panel border border-ui-border shadow-md rounded-lg px-4 py-2 flex items-center justify-between">
|
| 66 |
<div className="flex items-center min-w-0">
|
| 67 |
<span className="mr-2 text-ui-navy">💬</span>
|
| 68 |
<div className="min-w-0">
|
| 69 |
{loading ? (
|
| 70 |
+
<div className="text-sm text-ui-text/70">Loading…</div>
|
| 71 |
) : error ? (
|
| 72 |
+
<div className="text-sm text-red-400">{error}</div>
|
| 73 |
) : (
|
| 74 |
+
<div className="text-sm text-ui-text truncate">
|
| 75 |
{quote ? `『${quote.hitokoto}』 — ${quote.from_who || quote.from || 'Hitokoto'}` : ' '}
|
| 76 |
</div>
|
| 77 |
)}
|
|
|
|
| 80 |
<div className="flex items-center space-x-2 flex-shrink-0">
|
| 81 |
<button
|
| 82 |
onClick={fetchQuote}
|
| 83 |
+
className="text-xs px-2 py-1 rounded bg-ui-panel hover:bg-ui-panel/80 border border-ui-border text-ui-text"
|
| 84 |
title="Next quote"
|
| 85 |
>
|
| 86 |
Next
|
| 87 |
</button>
|
| 88 |
<button
|
| 89 |
onClick={() => { setHidden(true); localStorage.setItem(LOCAL_HIDE_KEY, '1'); }}
|
| 90 |
+
className="text-xs px-2 py-1 rounded bg-ui-panel hover:bg-ui-panel/80 border border-ui-border text-ui-text"
|
| 91 |
title="Hide"
|
| 92 |
>
|
| 93 |
Hide
|
client/src/components/Layout.tsx
CHANGED
|
@@ -163,11 +163,11 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 163 |
}
|
| 164 |
|
| 165 |
return (
|
| 166 |
-
<div className="min-h-screen bg-ui-bg text-ui-
|
| 167 |
{/* Top Bar */}
|
| 168 |
-
<header className="sticky top-0 z-40 bg-
|
| 169 |
<div className="px-4 sm:px-6 lg:px-8 h-14 flex items-center justify-between">
|
| 170 |
-
<Link to="/dashboard" className="text-lg font-bold text-ui-
|
| 171 |
<div />
|
| 172 |
</div>
|
| 173 |
</header>
|
|
@@ -175,7 +175,7 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 175 |
{/* Shell: Sidebar + Content */}
|
| 176 |
<div className="flex">
|
| 177 |
{/* Sidebar */}
|
| 178 |
-
<aside className="hidden md:flex md:flex-col w-60 min-h-[calc(100vh-56px)] border-r border-ui-
|
| 179 |
<nav className="p-3 space-y-1 flex-1">
|
| 180 |
{navigation.map((item) => {
|
| 181 |
const isActive = location.pathname === item.href;
|
|
@@ -184,7 +184,7 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 184 |
key={item.name}
|
| 185 |
to={item.href}
|
| 186 |
className={`flex items-center px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
| 187 |
-
isActive ? 'bg-
|
| 188 |
}`}
|
| 189 |
>
|
| 190 |
<item.icon className="h-4 w-4 mr-2" />
|
|
@@ -193,14 +193,14 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 193 |
);
|
| 194 |
})}
|
| 195 |
</nav>
|
| 196 |
-
<div className="p-3 border-t border-ui-
|
| 197 |
{user ? (
|
| 198 |
-
<button onClick={handleLogout} className="w-full flex items-center justify-start px-3 py-2 rounded-md text-sm font-medium text-ui-
|
| 199 |
<PowerIcon className="h-4 w-4 mr-2" />
|
| 200 |
Log Out
|
| 201 |
</button>
|
| 202 |
) : (
|
| 203 |
-
<Link to="/login" className="w-full flex items-center justify-start px-3 py-2 rounded-md text-sm font-medium text-ui-
|
| 204 |
<PowerIcon className="h-4 w-4 mr-2" />
|
| 205 |
Log In
|
| 206 |
</Link>
|
|
@@ -217,9 +217,9 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 217 |
{/* Transition Loading Indicator */}
|
| 218 |
{isTransitioning && (
|
| 219 |
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50">
|
| 220 |
-
<div className="bg-
|
| 221 |
-
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-ui-
|
| 222 |
-
<span className="text-ui-
|
| 223 |
</div>
|
| 224 |
</div>
|
| 225 |
)}
|
|
|
|
| 163 |
}
|
| 164 |
|
| 165 |
return (
|
| 166 |
+
<div className="min-h-screen bg-ui-bg text-ui-text">
|
| 167 |
{/* Top Bar */}
|
| 168 |
+
<header className="sticky top-0 z-40 bg-ui-panel/80 backdrop-blur border-b border-ui-border">
|
| 169 |
<div className="px-4 sm:px-6 lg:px-8 h-14 flex items-center justify-between">
|
| 170 |
+
<Link to="/dashboard" className="text-lg font-bold text-ui-neonCyan">TransHub</Link>
|
| 171 |
<div />
|
| 172 |
</div>
|
| 173 |
</header>
|
|
|
|
| 175 |
{/* Shell: Sidebar + Content */}
|
| 176 |
<div className="flex">
|
| 177 |
{/* Sidebar */}
|
| 178 |
+
<aside className="hidden md:flex md:flex-col w-60 min-h-[calc(100vh-56px)] border-r border-ui-border bg-ui-panel">
|
| 179 |
<nav className="p-3 space-y-1 flex-1">
|
| 180 |
{navigation.map((item) => {
|
| 181 |
const isActive = location.pathname === item.href;
|
|
|
|
| 184 |
key={item.name}
|
| 185 |
to={item.href}
|
| 186 |
className={`flex items-center px-3 py-2 rounded-md text-sm font-medium transition-colors ${
|
| 187 |
+
isActive ? 'bg-ui-bg text-ui-text neon-border' : 'text-ui-text/80 hover:bg-ui-panel/60'
|
| 188 |
}`}
|
| 189 |
>
|
| 190 |
<item.icon className="h-4 w-4 mr-2" />
|
|
|
|
| 193 |
);
|
| 194 |
})}
|
| 195 |
</nav>
|
| 196 |
+
<div className="p-3 border-t border-ui-border">
|
| 197 |
{user ? (
|
| 198 |
+
<button onClick={handleLogout} className="w-full flex items-center justify-start px-3 py-2 rounded-md text-sm font-medium text-ui-text/80 hover:bg-ui-panel/60">
|
| 199 |
<PowerIcon className="h-4 w-4 mr-2" />
|
| 200 |
Log Out
|
| 201 |
</button>
|
| 202 |
) : (
|
| 203 |
+
<Link to="/login" className="w-full flex items-center justify-start px-3 py-2 rounded-md text-sm font-medium text-ui-text/80 hover:bg-ui-panel/60">
|
| 204 |
<PowerIcon className="h-4 w-4 mr-2" />
|
| 205 |
Log In
|
| 206 |
</Link>
|
|
|
|
| 217 |
{/* Transition Loading Indicator */}
|
| 218 |
{isTransitioning && (
|
| 219 |
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50">
|
| 220 |
+
<div className="bg-ui-panel rounded-lg shadow-lg p-4 flex items-center space-x-3 border border-ui-border">
|
| 221 |
+
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-ui-neonCyan"></div>
|
| 222 |
+
<span className="text-ui-text font-medium">Loading...</span>
|
| 223 |
</div>
|
| 224 |
</div>
|
| 225 |
)}
|
client/src/index.css
CHANGED
|
@@ -7,17 +7,17 @@
|
|
| 7 |
font-family: 'Inter', system-ui, sans-serif;
|
| 8 |
}
|
| 9 |
body {
|
| 10 |
-
@apply bg-ui-bg text-ui-
|
| 11 |
}
|
| 12 |
}
|
| 13 |
|
| 14 |
@layer components {
|
| 15 |
.btn-primary {
|
| 16 |
-
@apply bg-ui-
|
| 17 |
}
|
| 18 |
|
| 19 |
.btn-secondary {
|
| 20 |
-
@apply bg-
|
| 21 |
}
|
| 22 |
|
| 23 |
.btn-danger {
|
|
@@ -25,11 +25,11 @@
|
|
| 25 |
}
|
| 26 |
|
| 27 |
.input-field {
|
| 28 |
-
@apply w-full px-3 py-2 border border-ui-
|
| 29 |
}
|
| 30 |
|
| 31 |
.card {
|
| 32 |
-
@apply bg-
|
| 33 |
}
|
| 34 |
|
| 35 |
.highlight-cultural {
|
|
@@ -37,7 +37,11 @@
|
|
| 37 |
}
|
| 38 |
|
| 39 |
.text-gradient {
|
| 40 |
-
@apply bg-gradient-to-r from-ui-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
}
|
| 42 |
|
| 43 |
.font-smiley {
|
|
|
|
| 7 |
font-family: 'Inter', system-ui, sans-serif;
|
| 8 |
}
|
| 9 |
body {
|
| 10 |
+
@apply bg-ui-bg text-ui-text;
|
| 11 |
}
|
| 12 |
}
|
| 13 |
|
| 14 |
@layer components {
|
| 15 |
.btn-primary {
|
| 16 |
+
@apply bg-ui-neonPink hover:bg-ui-neonPink/90 text-ui-bg font-semibold py-2 px-4 rounded-lg transition-colors duration-200 shadow-[0_0_12px_rgba(255,45,172,0.7)];
|
| 17 |
}
|
| 18 |
|
| 19 |
.btn-secondary {
|
| 20 |
+
@apply bg-ui-panel hover:bg-ui-panel/80 text-ui-text font-medium py-2 px-4 rounded-lg border border-ui-border transition-colors duration-200;
|
| 21 |
}
|
| 22 |
|
| 23 |
.btn-danger {
|
|
|
|
| 25 |
}
|
| 26 |
|
| 27 |
.input-field {
|
| 28 |
+
@apply w-full px-3 py-2 border border-ui-border rounded-lg bg-ui-panel text-ui-text placeholder-ui-text/50 focus:outline-none focus:ring-2 focus:ring-ui-neonCyan focus:border-transparent;
|
| 29 |
}
|
| 30 |
|
| 31 |
.card {
|
| 32 |
+
@apply bg-ui-panel rounded-xl shadow-sm border border-ui-border p-6;
|
| 33 |
}
|
| 34 |
|
| 35 |
.highlight-cultural {
|
|
|
|
| 37 |
}
|
| 38 |
|
| 39 |
.text-gradient {
|
| 40 |
+
@apply bg-gradient-to-r from-ui-neonPink to-ui-neonCyan bg-clip-text text-transparent;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.neon-border {
|
| 44 |
+
box-shadow: 0 0 0 1px theme('colors.ui.border') inset, 0 0 16px rgba(41,243,255,0.35);
|
| 45 |
}
|
| 46 |
|
| 47 |
.font-smiley {
|
client/src/pages/Dashboard.tsx
CHANGED
|
@@ -113,15 +113,15 @@ const Dashboard: React.FC = () => {
|
|
| 113 |
<div className="mb-8">
|
| 114 |
<div className="flex items-center justify-between">
|
| 115 |
<div>
|
| 116 |
-
<h1 className="text-3xl font-bold text-ui-
|
| 117 |
-
<p className="text-ui-
|
| 118 |
Ready to practice your translation skills?
|
| 119 |
</p>
|
| 120 |
</div>
|
| 121 |
<div className="flex items-center space-x-3">
|
| 122 |
-
<span className="text-sm text-ui-
|
| 123 |
{user.role === 'admin' && (
|
| 124 |
-
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-ui-
|
| 125 |
Admin
|
| 126 |
</span>
|
| 127 |
)}
|
|
@@ -131,21 +131,21 @@ const Dashboard: React.FC = () => {
|
|
| 131 |
|
| 132 |
{/* Quick Actions */}
|
| 133 |
<div className="mb-8">
|
| 134 |
-
<h2 className="text-lg font-semibold text-ui-
|
| 135 |
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
| 136 |
{actionsToShow.map((action) => (
|
| 137 |
<Link
|
| 138 |
key={action.name}
|
| 139 |
to={action.href}
|
| 140 |
-
className="bg-
|
| 141 |
>
|
| 142 |
<div className="flex items-center">
|
| 143 |
-
<div className={`p-3 rounded-lg bg-ui-
|
| 144 |
-
<action.icon className="h-6 w-6 text-
|
| 145 |
</div>
|
| 146 |
<div className="ml-4">
|
| 147 |
-
<h3 className="text-lg font-medium text-ui-
|
| 148 |
-
<p className="text-ui-
|
| 149 |
</div>
|
| 150 |
</div>
|
| 151 |
</Link>
|
|
@@ -176,23 +176,23 @@ const Dashboard: React.FC = () => {
|
|
| 176 |
)}
|
| 177 |
|
| 178 |
{/* Overview */}
|
| 179 |
-
<div className="bg-
|
| 180 |
<div className="flex items-center mb-4">
|
| 181 |
-
<ChartBarIcon className="h-6 w-6 text-ui-
|
| 182 |
-
<h3 className="text-lg font-semibold text-ui-
|
| 183 |
</div>
|
| 184 |
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
| 185 |
<div className="text-center">
|
| 186 |
-
<div className="text-2xl font-bold text-ui-
|
| 187 |
-
<div className="text-sm text-ui-
|
| 188 |
</div>
|
| 189 |
<div className="text-center">
|
| 190 |
-
<div className="text-2xl font-bold text-ui-
|
| 191 |
-
<div className="text-sm text-ui-
|
| 192 |
</div>
|
| 193 |
<div className="text-center">
|
| 194 |
-
<div className="text-2xl font-bold text-ui-
|
| 195 |
-
<div className="text-sm text-ui-
|
| 196 |
</div>
|
| 197 |
</div>
|
| 198 |
</div>
|
|
|
|
| 113 |
<div className="mb-8">
|
| 114 |
<div className="flex items-center justify-between">
|
| 115 |
<div>
|
| 116 |
+
<h1 className="text-3xl font-bold text-ui-text">{getGreeting()}</h1>
|
| 117 |
+
<p className="text-ui-text/70 mt-2">
|
| 118 |
Ready to practice your translation skills?
|
| 119 |
</p>
|
| 120 |
</div>
|
| 121 |
<div className="flex items-center space-x-3">
|
| 122 |
+
<span className="text-sm text-ui-text/70">{getRoleDisplay()}</span>
|
| 123 |
{user.role === 'admin' && (
|
| 124 |
+
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-ui-panel text-ui-text border border-ui-border">
|
| 125 |
Admin
|
| 126 |
</span>
|
| 127 |
)}
|
|
|
|
| 131 |
|
| 132 |
{/* Quick Actions */}
|
| 133 |
<div className="mb-8">
|
| 134 |
+
<h2 className="text-lg font-semibold text-ui-text mb-4">Quick Actions</h2>
|
| 135 |
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
| 136 |
{actionsToShow.map((action) => (
|
| 137 |
<Link
|
| 138 |
key={action.name}
|
| 139 |
to={action.href}
|
| 140 |
+
className="bg-ui-panel rounded-xl shadow-sm border border-ui-border p-6 hover:shadow-md transition neon-border"
|
| 141 |
>
|
| 142 |
<div className="flex items-center">
|
| 143 |
+
<div className={`p-3 rounded-lg bg-ui-neonPink animate-pulse-glow`}>
|
| 144 |
+
<action.icon className="h-6 w-6 text-ui-bg" />
|
| 145 |
</div>
|
| 146 |
<div className="ml-4">
|
| 147 |
+
<h3 className="text-lg font-medium text-ui-text">{action.name}</h3>
|
| 148 |
+
<p className="text-ui-text/70">{action.description}</p>
|
| 149 |
</div>
|
| 150 |
</div>
|
| 151 |
</Link>
|
|
|
|
| 176 |
)}
|
| 177 |
|
| 178 |
{/* Overview */}
|
| 179 |
+
<div className="bg-ui-panel rounded-xl shadow-sm border border-ui-border p-6">
|
| 180 |
<div className="flex items-center mb-4">
|
| 181 |
+
<ChartBarIcon className="h-6 w-6 text-ui-neonCyan mr-3" />
|
| 182 |
+
<h3 className="text-lg font-semibold text-ui-text">Course Overview</h3>
|
| 183 |
</div>
|
| 184 |
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
| 185 |
<div className="text-center">
|
| 186 |
+
<div className="text-2xl font-bold text-ui-neonCyan">6</div>
|
| 187 |
+
<div className="text-sm text-ui-text/70">Weeks</div>
|
| 188 |
</div>
|
| 189 |
<div className="text-center">
|
| 190 |
+
<div className="text-2xl font-bold text-ui-neonCyan">2</div>
|
| 191 |
+
<div className="text-sm text-ui-text/70">Task Types</div>
|
| 192 |
</div>
|
| 193 |
<div className="text-center">
|
| 194 |
+
<div className="text-2xl font-bold text-ui-neonCyan">Voting</div>
|
| 195 |
+
<div className="text-sm text-ui-text/70">Peer Review</div>
|
| 196 |
</div>
|
| 197 |
</div>
|
| 198 |
</div>
|
client/tailwind.config.js
CHANGED
|
@@ -6,12 +6,18 @@ module.exports = {
|
|
| 6 |
theme: {
|
| 7 |
extend: {
|
| 8 |
colors: {
|
| 9 |
-
//
|
| 10 |
ui: {
|
| 11 |
-
bg: '#
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
},
|
| 16 |
},
|
| 17 |
fontFamily: {
|
|
@@ -21,6 +27,7 @@ module.exports = {
|
|
| 21 |
'fade-in': 'fadeIn 0.3s ease-in-out',
|
| 22 |
'slide-in': 'slideIn 0.3s ease-out',
|
| 23 |
'bounce-in': 'bounceIn 0.6s ease-out',
|
|
|
|
| 24 |
},
|
| 25 |
keyframes: {
|
| 26 |
fadeIn: {
|
|
@@ -37,6 +44,10 @@ module.exports = {
|
|
| 37 |
'70%': { transform: 'scale(0.9)' },
|
| 38 |
'100%': { transform: 'scale(1)', opacity: '1' },
|
| 39 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
},
|
| 41 |
},
|
| 42 |
},
|
|
|
|
| 6 |
theme: {
|
| 7 |
extend: {
|
| 8 |
colors: {
|
| 9 |
+
// Cyberpunk palette
|
| 10 |
ui: {
|
| 11 |
+
bg: '#0A0B10', // near-black background
|
| 12 |
+
panel: '#121424', // dark panel
|
| 13 |
+
neonPink: '#FF2DAC', // neon primary
|
| 14 |
+
neonCyan: '#29F3FF', // neon secondary
|
| 15 |
+
neonLime: '#C7FF41', // highlights
|
| 16 |
+
magenta: '#D700FF', // accents
|
| 17 |
+
warning: '#FFB020', // warnings
|
| 18 |
+
text: '#E6E8FF', // primary text
|
| 19 |
+
textDim: '#A7ACD9', // secondary text
|
| 20 |
+
border: '#1F2238', // borders
|
| 21 |
},
|
| 22 |
},
|
| 23 |
fontFamily: {
|
|
|
|
| 27 |
'fade-in': 'fadeIn 0.3s ease-in-out',
|
| 28 |
'slide-in': 'slideIn 0.3s ease-out',
|
| 29 |
'bounce-in': 'bounceIn 0.6s ease-out',
|
| 30 |
+
'pulse-glow': 'pulseGlow 2s ease-in-out infinite',
|
| 31 |
},
|
| 32 |
keyframes: {
|
| 33 |
fadeIn: {
|
|
|
|
| 44 |
'70%': { transform: 'scale(0.9)' },
|
| 45 |
'100%': { transform: 'scale(1)', opacity: '1' },
|
| 46 |
},
|
| 47 |
+
pulseGlow: {
|
| 48 |
+
'0%, 100%': { boxShadow: '0 0 0px rgba(255, 45, 172, 0.0)' },
|
| 49 |
+
'50%': { boxShadow: '0 0 24px rgba(255, 45, 172, 0.6)' },
|
| 50 |
+
},
|
| 51 |
},
|
| 52 |
},
|
| 53 |
},
|