Upload folder using huggingface_hub
Browse files
client/src/pages/TutorialTasks.tsx
CHANGED
|
@@ -85,10 +85,12 @@ const TutorialTasks: React.FC = () => {
|
|
| 85 |
try {
|
| 86 |
const y = window.scrollY;
|
| 87 |
fn();
|
| 88 |
-
// Restore scroll position after DOM updates
|
| 89 |
requestAnimationFrame(() => {
|
| 90 |
requestAnimationFrame(() => {
|
| 91 |
-
|
|
|
|
|
|
|
| 92 |
});
|
| 93 |
});
|
| 94 |
} catch {
|
|
|
|
| 85 |
try {
|
| 86 |
const y = window.scrollY;
|
| 87 |
fn();
|
| 88 |
+
// Restore scroll position after DOM updates with multiple frames
|
| 89 |
requestAnimationFrame(() => {
|
| 90 |
requestAnimationFrame(() => {
|
| 91 |
+
requestAnimationFrame(() => {
|
| 92 |
+
window.scrollTo(0, y);
|
| 93 |
+
});
|
| 94 |
});
|
| 95 |
});
|
| 96 |
} catch {
|
client/src/pages/WeeklyPractice.tsx
CHANGED
|
@@ -1744,273 +1744,12 @@ const WeeklyPractice: React.FC = () => {
|
|
| 1744 |
</div>
|
| 1745 |
)}
|
| 1746 |
|
| 1747 |
-
{/*
|
| 1748 |
-
{!isWeekTransitioning && selectedWeek === 2 && (isAdmin || !isWeekHidden)
|
| 1749 |
-
<div className="
|
| 1750 |
-
<
|
| 1751 |
-
|
| 1752 |
-
|
| 1753 |
-
<h4 className="text-ui-text font-semibold text-base mb-2">Translation Brief</h4>
|
| 1754 |
-
<div className="text-ui-text leading-relaxed whitespace-pre-wrap">{renderFormatted(weeklyPracticeWeek.translationBrief)}</div>
|
| 1755 |
-
</div>
|
| 1756 |
-
) : null}
|
| 1757 |
-
{((localStorage.getItem('viewMode')||'auto') !== 'student' && (JSON.parse(localStorage.getItem('user')||'{}').role === 'admin')) && (
|
| 1758 |
-
<div className="mb-4">
|
| 1759 |
-
<button onClick={() => setShowSubmissions(v=>!v)} className="px-3 py-1.5 text-sm rounded-md border border-gray-300">{showSubmissions ? 'Hide Subtitling UI' : 'Show Subtitling UI (Admin)'}</button>
|
| 1760 |
-
</div>
|
| 1761 |
-
)}
|
| 1762 |
-
{weeklyPractice.length > 0 ? weeklyPractice.map((practice) => (
|
| 1763 |
-
<div key={practice._id} className="bg-white rounded-xl shadow-lg border border-gray-100 p-8 hover:shadow-xl transition-shadow duration-300">
|
| 1764 |
-
<div className="mb-6">
|
| 1765 |
-
<div className="flex items-center justify-between mb-4">
|
| 1766 |
-
<div className="flex items-center space-x-3">
|
| 1767 |
-
<div className="bg-pink-100 rounded-full p-2">
|
| 1768 |
-
<DocumentTextIcon className="h-5 w-5 text-pink-600" />
|
| 1769 |
-
</div>
|
| 1770 |
-
<div>
|
| 1771 |
-
<h3 className="text-lg font-semibold text-gray-900">Source Text #{weeklyPractice.indexOf(practice) + 1}</h3>
|
| 1772 |
-
</div>
|
| 1773 |
-
</div>
|
| 1774 |
-
{((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
|
| 1775 |
-
<div className="flex items-center space-x-2">
|
| 1776 |
-
{editingPractice === practice._id ? (
|
| 1777 |
-
<>
|
| 1778 |
-
<button
|
| 1779 |
-
onClick={savePractice}
|
| 1780 |
-
disabled={saving}
|
| 1781 |
-
className="bg-green-100 hover:bg-green-200 text-green-700 px-3 py-1 rounded-lg transition-colors duration-200"
|
| 1782 |
-
>
|
| 1783 |
-
{saving ? (
|
| 1784 |
-
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-green-600"></div>
|
| 1785 |
-
) : (
|
| 1786 |
-
<CheckIcon className="h-4 w-4" />
|
| 1787 |
-
)}
|
| 1788 |
-
</button>
|
| 1789 |
-
<button
|
| 1790 |
-
onClick={cancelEditing}
|
| 1791 |
-
className="bg-red-100 hover:bg-red-200 text-red-700 px-3 py-1 rounded-lg transition-colors duration-200"
|
| 1792 |
-
>
|
| 1793 |
-
<XMarkIcon className="h-4 w-4" />
|
| 1794 |
-
</button>
|
| 1795 |
-
</>
|
| 1796 |
-
) : (
|
| 1797 |
-
<>
|
| 1798 |
-
<button
|
| 1799 |
-
onClick={() => startEditing(practice)}
|
| 1800 |
-
className="bg-blue-100 hover:bg-blue-200 text-blue-700 px-3 py-1 rounded-lg transition-colors duration-200"
|
| 1801 |
-
>
|
| 1802 |
-
<PencilIcon className="h-4 w-4" />
|
| 1803 |
-
</button>
|
| 1804 |
-
<button
|
| 1805 |
-
onClick={() => deletePractice(practice._id)}
|
| 1806 |
-
className="bg-red-100 hover:bg-red-200 text-red-700 px-3 py-1 rounded-lg transition-colors duration-200"
|
| 1807 |
-
>
|
| 1808 |
-
<TrashIcon className="h-4 w-4" />
|
| 1809 |
-
</button>
|
| 1810 |
-
</>
|
| 1811 |
-
)}
|
| 1812 |
-
</div>
|
| 1813 |
-
)}
|
| 1814 |
-
</div>
|
| 1815 |
-
|
| 1816 |
-
{/* Content - Gradient underlay + glass overlay */}
|
| 1817 |
-
<div className="relative rounded-xl mb-6 p-0 border border-pink-200/60">
|
| 1818 |
-
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-pink-200/45 via-rose-200/40 to-pink-300/45" />
|
| 1819 |
-
<div className="relative rounded-xl bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)] p-6">
|
| 1820 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl opacity-50 [background:linear-gradient(to_bottom,rgba(255,255,255,0.3),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.28),rgba(255,255,255,0)_28%)]" />
|
| 1821 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-45" />
|
| 1822 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl" style={{ background: 'radial-gradient(120% 120% at 50% 55%, rgba(0,0,0,0.03), rgba(0,0,0,0) 60%)' }} />
|
| 1823 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl bg-pink-500/10 mix-blend-overlay opacity-25" />
|
| 1824 |
-
{editingPractice === practice._id ? (
|
| 1825 |
-
<div className="space-y-4">
|
| 1826 |
-
<textarea
|
| 1827 |
-
value={editForm.content}
|
| 1828 |
-
onChange={(e) => setEditForm({...editForm, content: e.target.value})}
|
| 1829 |
-
className="w-full px-4 py-3 border border-orange-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-orange-500 focus:border-orange-500 bg-white"
|
| 1830 |
-
rows={5}
|
| 1831 |
-
placeholder="Enter source text..."
|
| 1832 |
-
/>
|
| 1833 |
-
{selectedWeek >= 2 && (
|
| 1834 |
-
<div className="space-y-3">
|
| 1835 |
-
<div>
|
| 1836 |
-
<label className="block text-sm font-medium text-orange-700 mb-1">Image URL</label>
|
| 1837 |
-
<input
|
| 1838 |
-
type="text"
|
| 1839 |
-
value={editForm.imageUrl}
|
| 1840 |
-
onChange={(e) => setEditForm({...editForm, imageUrl: e.target.value})}
|
| 1841 |
-
className="w-full px-3 py-2 border border-orange-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
| 1842 |
-
placeholder="Enter image URL..."
|
| 1843 |
-
/>
|
| 1844 |
-
</div>
|
| 1845 |
-
<div>
|
| 1846 |
-
<label className="block text-sm font-medium text-orange-700 mb-1">Image Alt Text</label>
|
| 1847 |
-
<input
|
| 1848 |
-
type="text"
|
| 1849 |
-
value={editForm.imageAlt}
|
| 1850 |
-
onChange={(e) => setEditForm({...editForm, imageAlt: e.target.value})}
|
| 1851 |
-
className="w-full px-3 py-2 border border-orange-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
| 1852 |
-
placeholder="Enter alt text for accessibility..."
|
| 1853 |
-
/>
|
| 1854 |
-
</div>
|
| 1855 |
-
<div>
|
| 1856 |
-
<label className="block text-sm font-medium text-orange-700 mb-1">Upload Local Image</label>
|
| 1857 |
-
<input
|
| 1858 |
-
type="file"
|
| 1859 |
-
accept="image/*"
|
| 1860 |
-
onChange={async (e) => {
|
| 1861 |
-
const file = e.target.files?.[0];
|
| 1862 |
-
if (file) {
|
| 1863 |
-
try {
|
| 1864 |
-
const imageUrl = await handleFileUpload(file);
|
| 1865 |
-
setEditForm({ ...editForm, imageUrl });
|
| 1866 |
-
} catch (error) {
|
| 1867 |
-
console.error('Error uploading file:', error);
|
| 1868 |
-
}
|
| 1869 |
-
}
|
| 1870 |
-
}}
|
| 1871 |
-
className="w-full px-3 py-2 border border-orange-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500"
|
| 1872 |
-
/>
|
| 1873 |
-
{uploading && (
|
| 1874 |
-
<div className="mt-2 text-sm text-orange-600">
|
| 1875 |
-
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-orange-600 inline-block mr-2"></div>
|
| 1876 |
-
Uploading...
|
| 1877 |
-
</div>
|
| 1878 |
-
)}
|
| 1879 |
-
</div>
|
| 1880 |
-
</div>
|
| 1881 |
-
)}
|
| 1882 |
-
</div>
|
| 1883 |
-
) : (
|
| 1884 |
-
<div>
|
| 1885 |
-
{practice.imageUrl ? (
|
| 1886 |
-
selectedWeek >= 4 && practice.imageAlignment === 'portrait-split' ? (
|
| 1887 |
-
// Portrait split layout
|
| 1888 |
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-start">
|
| 1889 |
-
<div className="w-full flex justify-center">
|
| 1890 |
-
<div className="inline-block rounded-lg shadow-md overflow-hidden">
|
| 1891 |
-
<img src={practice.imageUrl} alt={practice.imageAlt || 'Uploaded image'} className="w-full h-auto" style={{ maxHeight: '520px', objectFit: 'contain' }} />
|
| 1892 |
-
</div>
|
| 1893 |
-
</div>
|
| 1894 |
-
<div className="w-full">
|
| 1895 |
-
<div className="mb-4 text-ui-text leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</div>
|
| 1896 |
-
{localStorage.getItem('token') && (
|
| 1897 |
-
<div className="relative rounded-xl bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)] p-4">
|
| 1898 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl opacity-50 [background:linear-gradient(to_bottom,rgba(255,255,255,0.3),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.28),rgba(255,255,255,0)_28%)]" />
|
| 1899 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-45" />
|
| 1900 |
-
<h5 className="relative z-10 text-ui-text font-semibold mb-2">Your Translation</h5>
|
| 1901 |
-
<div className="relative z-10 flex items-center justify-end space-x-2 mb-2">
|
| 1902 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '**')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">B</button>
|
| 1903 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '*')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded italic">I</button>
|
| 1904 |
-
<button onClick={() => applyLinkFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }))} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">Link</button>
|
| 1905 |
-
</div>
|
| 1906 |
-
<textarea id={`weekly-translation-${practice._id}`} value={translationText[practice._id] || ''} onChange={(e) => setTranslationText({ ...translationText, [practice._id]: e.target.value })} className="relative z-10 w-full px-4 py-3 border border-ui-border rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500 bg-white" rows={4} placeholder="Enter your translation here..." />
|
| 1907 |
-
<div className="relative z-10 flex justify-end mt-2">
|
| 1908 |
-
<button onClick={() => handleSubmitTranslation(practice._id)} disabled={submitting[practice._id]} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-pink-600/70 disabled:bg-gray-400 active:translate-y-0.5 active:shadow-[inset_0_0.5px_0_rgba(255,255,255,0.6),inset_0_-1px_0_rgba(0,0,0,0.12)] transition-all duration-200">
|
| 1909 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 1910 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 1911 |
-
{submitting[practice._id] ? (
|
| 1912 |
-
<>
|
| 1913 |
-
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
|
| 1914 |
-
Submitting...
|
| 1915 |
-
</>
|
| 1916 |
-
) : (
|
| 1917 |
-
<>
|
| 1918 |
-
<CheckIcon className="h-4 w-4" />
|
| 1919 |
-
Submit Translation
|
| 1920 |
-
</>
|
| 1921 |
-
)}
|
| 1922 |
-
</button>
|
| 1923 |
-
</div>
|
| 1924 |
-
</div>
|
| 1925 |
-
)}
|
| 1926 |
-
</div>
|
| 1927 |
-
</div>
|
| 1928 |
-
) : (
|
| 1929 |
-
// Regular image layout
|
| 1930 |
-
<div className="space-y-4">
|
| 1931 |
-
<div className="flex justify-center">
|
| 1932 |
-
<div className="inline-block rounded-lg shadow-md overflow-hidden">
|
| 1933 |
-
<img src={practice.imageUrl} alt={practice.imageAlt || 'Uploaded image'} className="w-full h-auto" style={{ maxHeight: '400px', objectFit: 'contain' }} />
|
| 1934 |
-
</div>
|
| 1935 |
-
</div>
|
| 1936 |
-
<div className="text-ui-text leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</div>
|
| 1937 |
-
{localStorage.getItem('token') && (
|
| 1938 |
-
<div className="relative rounded-xl bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)] p-4">
|
| 1939 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl opacity-50 [background:linear-gradient(to_bottom,rgba(255,255,255,0.3),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.28),rgba(255,255,255,0)_28%)]" />
|
| 1940 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-45" />
|
| 1941 |
-
<h5 className="relative z-10 text-ui-text font-semibold mb-2">Your Translation</h5>
|
| 1942 |
-
<div className="relative z-10 flex items-center justify-end space-x-2 mb-2">
|
| 1943 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '**')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">B</button>
|
| 1944 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '*')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded italic">I</button>
|
| 1945 |
-
<button onClick={() => applyLinkFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }))} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">Link</button>
|
| 1946 |
-
</div>
|
| 1947 |
-
<textarea id={`weekly-translation-${practice._id}`} value={translationText[practice._id] || ''} onChange={(e) => setTranslationText({ ...translationText, [practice._id]: e.target.value })} className="relative z-10 w-full px-4 py-3 border border-ui-border rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500 bg-white" rows={4} placeholder="Enter your translation here..." />
|
| 1948 |
-
<div className="relative z-10 flex justify-end mt-2">
|
| 1949 |
-
<button onClick={() => handleSubmitTranslation(practice._id)} disabled={submitting[practice._id]} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-pink-600/70 disabled:bg-gray-400 active:translate-y-0.5 active:shadow-[inset_0_0.5px_0_rgba(255,255,255,0.6),inset_0_-1px_0_rgba(0,0,0,0.12)] transition-all duration-200">
|
| 1950 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 1951 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 1952 |
-
{submitting[practice._id] ? (
|
| 1953 |
-
<>
|
| 1954 |
-
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
|
| 1955 |
-
Submitting...
|
| 1956 |
-
</>
|
| 1957 |
-
) : (
|
| 1958 |
-
<>
|
| 1959 |
-
<CheckIcon className="h-4 w-4" />
|
| 1960 |
-
Submit Translation
|
| 1961 |
-
</>
|
| 1962 |
-
)}
|
| 1963 |
-
</button>
|
| 1964 |
-
</div>
|
| 1965 |
-
</div>
|
| 1966 |
-
)}
|
| 1967 |
-
</div>
|
| 1968 |
-
)
|
| 1969 |
-
) : (
|
| 1970 |
-
// Text-only layout
|
| 1971 |
-
<div className="mb-4 text-ui-text leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</div>
|
| 1972 |
-
)}
|
| 1973 |
-
{localStorage.getItem('token') && !practice.imageUrl && (
|
| 1974 |
-
<div className="relative rounded-xl bg-white/10 backdrop-blur-md ring-1 ring-inset ring-white/30 shadow-[inset_0_0.5px_0_rgba(255,255,255,0.5),inset_0_-1px_1.5px_rgba(0,0,0,0.12)] p-4">
|
| 1975 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl opacity-50 [background:linear-gradient(to_bottom,rgba(255,255,255,0.3),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.28),rgba(255,255,255,0)_28%)]" />
|
| 1976 |
-
<div className="pointer-events-none absolute inset-0 rounded-xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-45" />
|
| 1977 |
-
<h5 className="relative z-10 text-ui-text font-semibold mb-2">Your Translation</h5>
|
| 1978 |
-
<div className="relative z-10 flex items-center justify-end space-x-2 mb-2">
|
| 1979 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '**')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">B</button>
|
| 1980 |
-
<button onClick={() => applyInlineFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }), '*')} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded italic">I</button>
|
| 1981 |
-
<button onClick={() => applyLinkFormat(`weekly-translation-${practice._id}`, translationText[practice._id] || '', v => setTranslationText({ ...translationText, [practice._id]: v }))} className="px-2 py-1 text-xs bg-white/40 text-ui-text rounded">Link</button>
|
| 1982 |
-
</div>
|
| 1983 |
-
<textarea id={`weekly-translation-${practice._id}`} value={translationText[practice._id] || ''} onChange={(e) => setTranslationText({ ...translationText, [practice._id]: e.target.value })} className="relative z-10 w-full px-4 py-3 border border-ui-border rounded-lg focus:outline-none focus:ring-2 focus:ring-pink-500 focus:border-pink-500 bg-white" rows={4} placeholder="Enter your translation here..." />
|
| 1984 |
-
<div className="relative z-10 flex justify-end mt-2">
|
| 1985 |
-
<button onClick={() => handleSubmitTranslation(practice._id)} disabled={submitting[practice._id]} className="relative overflow-hidden inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-2xl text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 bg-pink-600/70 disabled:bg-gray-400 active:translate-y-0.5 active:shadow-[inset_0_0.5px_0_rgba(255,255,255,0.6),inset_0_-1px_0_rgba(0,0,0,0.12)] transition-all duration-200">
|
| 1986 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl opacity-60 [background:linear-gradient(to_bottom,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%),linear-gradient(to_right,rgba(255,255,255,0.35),rgba(255,255,255,0)_28%)]" />
|
| 1987 |
-
<div className="pointer-events-none absolute inset-0 rounded-2xl bg-gradient-to-tr from-white/30 via-white/10 to-transparent opacity-50" />
|
| 1988 |
-
{submitting[practice._id] ? (
|
| 1989 |
-
<>
|
| 1990 |
-
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
|
| 1991 |
-
Submitting...
|
| 1992 |
-
</>
|
| 1993 |
-
) : (
|
| 1994 |
-
<>
|
| 1995 |
-
<CheckIcon className="h-4 w-4" />
|
| 1996 |
-
Submit Translation
|
| 1997 |
-
</>
|
| 1998 |
-
)}
|
| 1999 |
-
</button>
|
| 2000 |
-
</div>
|
| 2001 |
-
</div>
|
| 2002 |
-
)}
|
| 2003 |
-
</div>
|
| 2004 |
-
)}
|
| 2005 |
-
</div>
|
| 2006 |
-
</div>
|
| 2007 |
-
</div>
|
| 2008 |
-
</div>
|
| 2009 |
-
)) : (
|
| 2010 |
-
<div className="text-gray-600">No practice items for Week 2 yet.</div>
|
| 2011 |
-
)}
|
| 2012 |
-
</div>
|
| 2013 |
-
) : null}
|
| 2014 |
{((localStorage.getItem('viewMode')||'auto') !== 'student' && (JSON.parse(localStorage.getItem('user')||'{}').role === 'admin') && showSubmissions) && (
|
| 2015 |
<>
|
| 2016 |
<div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
|
|
@@ -2212,6 +1951,16 @@ const WeeklyPractice: React.FC = () => {
|
|
| 2212 |
</div>
|
| 2213 |
) : (
|
| 2214 |
<div className="space-y-6">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2215 |
{/* Add Practice Button for Admin */}
|
| 2216 |
{(() => {
|
| 2217 |
const user = JSON.parse(localStorage.getItem('user') || '{}');
|
|
|
|
| 1744 |
</div>
|
| 1745 |
)}
|
| 1746 |
|
| 1747 |
+
{/* Subtitling UI for Week 2 (admin only) */}
|
| 1748 |
+
{!isWeekTransitioning && selectedWeek === 2 && (isAdmin || !isWeekHidden) && showSubmissions && (
|
| 1749 |
+
<div className="mb-8">
|
| 1750 |
+
<div className="mb-4">
|
| 1751 |
+
<button onClick={() => setShowSubmissions(false)} className="px-3 py-1.5 text-sm rounded-md border border-gray-300">Hide Subtitling UI</button>
|
| 1752 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1753 |
{((localStorage.getItem('viewMode')||'auto') !== 'student' && (JSON.parse(localStorage.getItem('user')||'{}').role === 'admin') && showSubmissions) && (
|
| 1754 |
<>
|
| 1755 |
<div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
|
|
|
|
| 1951 |
</div>
|
| 1952 |
) : (
|
| 1953 |
<div className="space-y-6">
|
| 1954 |
+
{/* Show Subtitling UI Button for Week 2 Admin */}
|
| 1955 |
+
{selectedWeek === 2 && (() => {
|
| 1956 |
+
const user = JSON.parse(localStorage.getItem('user') || '{}');
|
| 1957 |
+
const viewMode = (localStorage.getItem('viewMode') || 'auto');
|
| 1958 |
+
return viewMode === 'student' ? false : user.role === 'admin';
|
| 1959 |
+
})() && (
|
| 1960 |
+
<div className="mb-4">
|
| 1961 |
+
<button onClick={() => setShowSubmissions(v=>!v)} className="px-3 py-1.5 text-sm rounded-md border border-gray-300">{showSubmissions ? 'Hide Subtitling UI' : 'Show Subtitling UI (Admin)'}</button>
|
| 1962 |
+
</div>
|
| 1963 |
+
)}
|
| 1964 |
{/* Add Practice Button for Admin */}
|
| 1965 |
{(() => {
|
| 1966 |
const user = JSON.parse(localStorage.getItem('user') || '{}');
|