Upload folder using huggingface_hub
Browse files
client/src/components/Layout.tsx
CHANGED
|
@@ -174,8 +174,8 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 174 |
navigation = navigation.filter(item => item.name !== 'Slides');
|
| 175 |
}
|
| 176 |
|
| 177 |
-
// Add Manage link for admin users (
|
| 178 |
-
if (user?.role === 'admin'
|
| 179 |
navigation.push({ name: 'Manage', href: '/manage', icon: Cog6ToothIcon });
|
| 180 |
}
|
| 181 |
|
|
@@ -202,17 +202,7 @@ const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
| 202 |
<img src="/favicon-512x512.png" alt="logo" className="h-8 w-8 mr-2" />
|
| 203 |
TransHub
|
| 204 |
</Link>
|
| 205 |
-
<div>
|
| 206 |
-
{user?.role === 'admin' && effectiveRole === 'student' && (
|
| 207 |
-
<Link
|
| 208 |
-
to="/manage"
|
| 209 |
-
className="inline-flex items-center px-3 py-1.5 rounded-md text-sm font-medium text-ui-text bg-white/60 hover:bg-white/80 ring-1 ring-inset ring-ui-border"
|
| 210 |
-
title="Open Manage while in Student view"
|
| 211 |
-
>
|
| 212 |
-
Manage
|
| 213 |
-
</Link>
|
| 214 |
-
)}
|
| 215 |
-
</div>
|
| 216 |
</div>
|
| 217 |
</header>
|
| 218 |
|
|
|
|
| 174 |
navigation = navigation.filter(item => item.name !== 'Slides');
|
| 175 |
}
|
| 176 |
|
| 177 |
+
// Add Manage link for admin users (always keep in nav)
|
| 178 |
+
if (user?.role === 'admin') {
|
| 179 |
navigation.push({ name: 'Manage', href: '/manage', icon: Cog6ToothIcon });
|
| 180 |
}
|
| 181 |
|
|
|
|
| 202 |
<img src="/favicon-512x512.png" alt="logo" className="h-8 w-8 mr-2" />
|
| 203 |
TransHub
|
| 204 |
</Link>
|
| 205 |
+
<div />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
</div>
|
| 207 |
</header>
|
| 208 |
|
client/src/pages/Dashboard.tsx
CHANGED
|
@@ -40,6 +40,8 @@ const Dashboard: React.FC = () => {
|
|
| 40 |
|
| 41 |
const getRoleDisplay = () => {
|
| 42 |
if (!user) return '';
|
|
|
|
|
|
|
| 43 |
return user.role === 'admin' ? 'Admin' : 'Student';
|
| 44 |
};
|
| 45 |
|
|
@@ -211,8 +213,8 @@ const Dashboard: React.FC = () => {
|
|
| 211 |
</div>
|
| 212 |
</div>
|
| 213 |
|
| 214 |
-
{/* Admin Panel (only for admin users) */}
|
| 215 |
-
{user.role === 'admin' && (
|
| 216 |
<div className="mb-8">
|
| 217 |
<h2 className="text-lg font-medium text-gray-900 mb-4">Admin Panel</h2>
|
| 218 |
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
|
|
| 40 |
|
| 41 |
const getRoleDisplay = () => {
|
| 42 |
if (!user) return '';
|
| 43 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 44 |
+
if (viewMode === 'student' && user.role === 'admin') return 'Student (view)';
|
| 45 |
return user.role === 'admin' ? 'Admin' : 'Student';
|
| 46 |
};
|
| 47 |
|
|
|
|
| 213 |
</div>
|
| 214 |
</div>
|
| 215 |
|
| 216 |
+
{/* Admin Panel (only for admin users; hidden in Student view) */}
|
| 217 |
+
{user.role === 'admin' && (localStorage.getItem('viewMode')||'auto') !== 'student' && (
|
| 218 |
<div className="mb-8">
|
| 219 |
<h2 className="text-lg font-medium text-gray-900 mb-4">Admin Panel</h2>
|
| 220 |
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
client/src/pages/Feedback.tsx
CHANGED
|
@@ -10,7 +10,12 @@ const Feedback: React.FC = () => {
|
|
| 10 |
|
| 11 |
useEffect(() => {
|
| 12 |
const u = localStorage.getItem('user');
|
| 13 |
-
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}, []);
|
| 15 |
|
| 16 |
const send = async () => {
|
|
|
|
| 10 |
|
| 11 |
useEffect(() => {
|
| 12 |
const u = localStorage.getItem('user');
|
| 13 |
+
try {
|
| 14 |
+
const parsed = u ? JSON.parse(u) : null;
|
| 15 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 16 |
+
const admin = parsed?.role === 'admin';
|
| 17 |
+
setIsAdmin(viewMode === 'student' ? false : admin);
|
| 18 |
+
} catch {}
|
| 19 |
}, []);
|
| 20 |
|
| 21 |
const send = async () => {
|
client/src/pages/Slides.tsx
CHANGED
|
@@ -31,7 +31,12 @@ const Slides: React.FC = () => {
|
|
| 31 |
let aborted = false;
|
| 32 |
(async () => {
|
| 33 |
const u = localStorage.getItem('user');
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
try {
|
| 36 |
setLoading(true); setError('');
|
| 37 |
const resp = await api.get('/api/slides', { headers: { 'Cache-Control': 'no-cache' } });
|
|
|
|
| 31 |
let aborted = false;
|
| 32 |
(async () => {
|
| 33 |
const u = localStorage.getItem('user');
|
| 34 |
+
try {
|
| 35 |
+
const parsed = u ? JSON.parse(u) : null;
|
| 36 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 37 |
+
const admin = parsed?.role === 'admin';
|
| 38 |
+
setIsAdmin(viewMode === 'student' ? false : admin);
|
| 39 |
+
} catch {}
|
| 40 |
try {
|
| 41 |
setLoading(true); setError('');
|
| 42 |
const resp = await api.get('/api/slides', { headers: { 'Cache-Control': 'no-cache' } });
|
client/src/pages/Toolkit.tsx
CHANGED
|
@@ -110,8 +110,9 @@ const Toolkit: React.FC = () => {
|
|
| 110 |
try {
|
| 111 |
const u = localStorage.getItem('user');
|
| 112 |
const role = u ? (JSON.parse(u)?.role || 'visitor') : 'visitor';
|
|
|
|
| 113 |
setIsVisitor(role === 'visitor');
|
| 114 |
-
setIsAdmin(role === 'admin');
|
| 115 |
} catch {
|
| 116 |
setIsVisitor(true);
|
| 117 |
setIsAdmin(false);
|
|
|
|
| 110 |
try {
|
| 111 |
const u = localStorage.getItem('user');
|
| 112 |
const role = u ? (JSON.parse(u)?.role || 'visitor') : 'visitor';
|
| 113 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 114 |
setIsVisitor(role === 'visitor');
|
| 115 |
+
setIsAdmin(viewMode === 'student' ? false : role === 'admin');
|
| 116 |
} catch {
|
| 117 |
setIsVisitor(true);
|
| 118 |
setIsAdmin(false);
|
client/src/pages/TutorialTasks.tsx
CHANGED
|
@@ -67,7 +67,9 @@ const TutorialTasks: React.FC = () => {
|
|
| 67 |
// Move a task up or down by normalizing positions for the current visible list (weeks 4–6 only)
|
| 68 |
const moveTask = async (taskId: string, direction: 'up' | 'down') => {
|
| 69 |
try {
|
| 70 |
-
const
|
|
|
|
|
|
|
| 71 |
if (!isAdmin || selectedWeek < 4) return;
|
| 72 |
// Build ordered list for the current week from what is rendered
|
| 73 |
const current = tutorialTasks.filter(t => t.weekNumber === selectedWeek);
|
|
@@ -124,7 +126,9 @@ const TutorialTasks: React.FC = () => {
|
|
| 124 |
const [urlInput, setUrlInput] = useState<string>('');
|
| 125 |
const [errorMsg, setErrorMsg] = useState<string>('');
|
| 126 |
const [copiedLink, setCopiedLink] = useState<string>('');
|
| 127 |
-
const
|
|
|
|
|
|
|
| 128 |
|
| 129 |
const CopySquaresIcon: React.FC<{ className?: string }> = ({ className }) => (
|
| 130 |
<svg className={className || 'h-4 w-4'} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
| 67 |
// Move a task up or down by normalizing positions for the current visible list (weeks 4–6 only)
|
| 68 |
const moveTask = async (taskId: string, direction: 'up' | 'down') => {
|
| 69 |
try {
|
| 70 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 71 |
+
const actualRole = (JSON.parse(localStorage.getItem('user') || '{}').role);
|
| 72 |
+
const isAdmin = viewMode === 'student' ? false : actualRole === 'admin';
|
| 73 |
if (!isAdmin || selectedWeek < 4) return;
|
| 74 |
// Build ordered list for the current week from what is rendered
|
| 75 |
const current = tutorialTasks.filter(t => t.weekNumber === selectedWeek);
|
|
|
|
| 126 |
const [urlInput, setUrlInput] = useState<string>('');
|
| 127 |
const [errorMsg, setErrorMsg] = useState<string>('');
|
| 128 |
const [copiedLink, setCopiedLink] = useState<string>('');
|
| 129 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 130 |
+
const actualRole = (JSON.parse(localStorage.getItem('user') || '{}').role);
|
| 131 |
+
const isAdmin = viewMode === 'student' ? false : actualRole === 'admin';
|
| 132 |
|
| 133 |
const CopySquaresIcon: React.FC<{ className?: string }> = ({ className }) => (
|
| 134 |
<svg className={className || 'h-4 w-4'} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" xmlns="http://www.w3.org/2000/svg">
|
client/src/pages/WeeklyPractice.tsx
CHANGED
|
@@ -1474,7 +1474,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1474 |
</div>
|
| 1475 |
<h3 className="text-ui-text font-semibold text-xl">Translation Brief</h3>
|
| 1476 |
</div>
|
| 1477 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1478 |
<div className="flex items-center space-x-2">
|
| 1479 |
{editingBrief[selectedWeek] ? (
|
| 1480 |
<>
|
|
@@ -1537,7 +1537,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1537 |
</div>
|
| 1538 |
) : (
|
| 1539 |
// Show add brief button when no brief exists
|
| 1540 |
-
JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1541 |
<div className="bg-ui-panel rounded-xl p-8 mb-8 border border-ui-border border-dashed">
|
| 1542 |
<div className="flex items-center justify-between mb-4">
|
| 1543 |
<div className="flex items-center space-x-2">
|
|
@@ -1594,7 +1594,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1594 |
)}
|
| 1595 |
|
| 1596 |
{/* Week Files (Week 6 uploads) */}
|
| 1597 |
-
{(weekFiles.source.length > 0 || weekFiles.translation.length > 0 || (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin')) && (
|
| 1598 |
<div className="bg-white rounded-xl shadow p-6 mb-8">
|
| 1599 |
<h3 className="text-lg font-semibold text-gray-900 mb-4">Week {selectedWeek} Files</h3>
|
| 1600 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-start content-start">
|
|
@@ -1624,7 +1624,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1624 |
<div className="min-w-0 truncate font-medium" title={f.fileName}>{f.fileName}</div>
|
| 1625 |
<div className="flex items-center space-x-3 flex-shrink-0">
|
| 1626 |
<button className="text-pink-600 hover:underline" onClick={() => downloadWeekFile(f._id, f.fileName)}>Download</button>
|
| 1627 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1628 |
<button
|
| 1629 |
className="text-red-600 hover:underline"
|
| 1630 |
onClick={async () => {
|
|
@@ -1675,7 +1675,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1675 |
<div className="min-w-0 truncate font-medium" title={f.fileName}>{f.fileName}</div>
|
| 1676 |
<div className="flex items-center space-x-3 flex-shrink-0">
|
| 1677 |
<button className="text-pink-600 hover:underline" onClick={() => downloadWeekFile(f._id, f.fileName)}>Download</button>
|
| 1678 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1679 |
<button
|
| 1680 |
className="text-red-600 hover:underline"
|
| 1681 |
onClick={async () => {
|
|
@@ -1761,7 +1761,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1761 |
)}
|
| 1762 |
|
| 1763 |
{/* Admin Time Code Editor */}
|
| 1764 |
-
{editingSegment && JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1765 |
<div className="bg-indigo-50 border border-indigo-200 rounded-lg p-4 mb-4">
|
| 1766 |
<h4 className="text-sm font-medium text-indigo-800 mb-2">Edit Time Codes for Segment {editingSegment}</h4>
|
| 1767 |
<div className="grid grid-cols-2 gap-2">
|
|
@@ -1815,7 +1815,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1815 |
<p className="text-gray-800">
|
| 1816 |
Segment {currentSegment}: {subtitleSegments[currentSegment - 1]?.startTime} – {subtitleSegments[currentSegment - 1]?.endTime} ({subtitleSegments[currentSegment - 1]?.duration})
|
| 1817 |
</p>
|
| 1818 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1819 |
<button
|
| 1820 |
onClick={() => handleEditTimeCode(currentSegment)}
|
| 1821 |
className="bg-indigo-600 hover:bg-indigo-700 text-white px-2 py-1 rounded text-xs flex items-center space-x-1"
|
|
@@ -1936,7 +1936,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1936 |
<span className="text-xs text-gray-500">
|
| 1937 |
{new Date(submission.submissionDate).toLocaleString()}
|
| 1938 |
</span>
|
| 1939 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 1940 |
<button
|
| 1941 |
onClick={() => handleDeleteSubtitleSubmission(submission._id)}
|
| 1942 |
className="text-red-500 hover:text-red-700 text-xs"
|
|
@@ -2264,7 +2264,7 @@ const WeeklyPractice: React.FC = () => {
|
|
| 2264 |
<h3 className="text-lg font-semibold text-gray-900">Source Text #{weeklyPractice.indexOf(practice) + 1}</h3>
|
| 2265 |
</div>
|
| 2266 |
</div>
|
| 2267 |
-
{JSON.parse(localStorage.getItem('user') || '{}').role === 'admin' && (
|
| 2268 |
<div className="flex items-center space-x-2">
|
| 2269 |
{editingPractice === practice._id ? (
|
| 2270 |
<>
|
|
|
|
| 1474 |
</div>
|
| 1475 |
<h3 className="text-ui-text font-semibold text-xl">Translation Brief</h3>
|
| 1476 |
</div>
|
| 1477 |
+
{(localStorage.getItem('viewMode')||'auto') === 'student' ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1478 |
<div className="flex items-center space-x-2">
|
| 1479 |
{editingBrief[selectedWeek] ? (
|
| 1480 |
<>
|
|
|
|
| 1537 |
</div>
|
| 1538 |
) : (
|
| 1539 |
// Show add brief button when no brief exists
|
| 1540 |
+
(localStorage.getItem('viewMode')||'auto') === 'student' ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1541 |
<div className="bg-ui-panel rounded-xl p-8 mb-8 border border-ui-border border-dashed">
|
| 1542 |
<div className="flex items-center justify-between mb-4">
|
| 1543 |
<div className="flex items-center space-x-2">
|
|
|
|
| 1594 |
)}
|
| 1595 |
|
| 1596 |
{/* Week Files (Week 6 uploads) */}
|
| 1597 |
+
{(weekFiles.source.length > 0 || weekFiles.translation.length > 0 || (((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin'))) && (
|
| 1598 |
<div className="bg-white rounded-xl shadow p-6 mb-8">
|
| 1599 |
<h3 className="text-lg font-semibold text-gray-900 mb-4">Week {selectedWeek} Files</h3>
|
| 1600 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-start content-start">
|
|
|
|
| 1624 |
<div className="min-w-0 truncate font-medium" title={f.fileName}>{f.fileName}</div>
|
| 1625 |
<div className="flex items-center space-x-3 flex-shrink-0">
|
| 1626 |
<button className="text-pink-600 hover:underline" onClick={() => downloadWeekFile(f._id, f.fileName)}>Download</button>
|
| 1627 |
+
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1628 |
<button
|
| 1629 |
className="text-red-600 hover:underline"
|
| 1630 |
onClick={async () => {
|
|
|
|
| 1675 |
<div className="min-w-0 truncate font-medium" title={f.fileName}>{f.fileName}</div>
|
| 1676 |
<div className="flex items-center space-x-3 flex-shrink-0">
|
| 1677 |
<button className="text-pink-600 hover:underline" onClick={() => downloadWeekFile(f._id, f.fileName)}>Download</button>
|
| 1678 |
+
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1679 |
<button
|
| 1680 |
className="text-red-600 hover:underline"
|
| 1681 |
onClick={async () => {
|
|
|
|
| 1761 |
)}
|
| 1762 |
|
| 1763 |
{/* Admin Time Code Editor */}
|
| 1764 |
+
{editingSegment && (((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin')) && (
|
| 1765 |
<div className="bg-indigo-50 border border-indigo-200 rounded-lg p-4 mb-4">
|
| 1766 |
<h4 className="text-sm font-medium text-indigo-800 mb-2">Edit Time Codes for Segment {editingSegment}</h4>
|
| 1767 |
<div className="grid grid-cols-2 gap-2">
|
|
|
|
| 1815 |
<p className="text-gray-800">
|
| 1816 |
Segment {currentSegment}: {subtitleSegments[currentSegment - 1]?.startTime} – {subtitleSegments[currentSegment - 1]?.endTime} ({subtitleSegments[currentSegment - 1]?.duration})
|
| 1817 |
</p>
|
| 1818 |
+
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1819 |
<button
|
| 1820 |
onClick={() => handleEditTimeCode(currentSegment)}
|
| 1821 |
className="bg-indigo-600 hover:bg-indigo-700 text-white px-2 py-1 rounded text-xs flex items-center space-x-1"
|
|
|
|
| 1936 |
<span className="text-xs text-gray-500">
|
| 1937 |
{new Date(submission.submissionDate).toLocaleString()}
|
| 1938 |
</span>
|
| 1939 |
+
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1940 |
<button
|
| 1941 |
onClick={() => handleDeleteSubtitleSubmission(submission._id)}
|
| 1942 |
className="text-red-500 hover:text-red-700 text-xs"
|
|
|
|
| 2264 |
<h3 className="text-lg font-semibold text-gray-900">Source Text #{weeklyPractice.indexOf(practice) + 1}</h3>
|
| 2265 |
</div>
|
| 2266 |
</div>
|
| 2267 |
+
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 2268 |
<div className="flex items-center space-x-2">
|
| 2269 |
{editingPractice === practice._id ? (
|
| 2270 |
<>
|