linguabot commited on
Commit
7df223b
·
verified ·
1 Parent(s): 138e263

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. client/src/pages/WeeklyPractice.tsx +196 -266
client/src/pages/WeeklyPractice.tsx CHANGED
@@ -1769,273 +1769,203 @@ const WeeklyPractice: React.FC = () => {
1769
  </div>
1770
  ) : null}
1771
  {((localStorage.getItem('viewMode')||'auto') !== 'student' && (JSON.parse(localStorage.getItem('user')||'{}').role === 'admin') && showSubmissions) && (
1772
- <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
1773
- <div className="mb-4">
1774
- <h3 className="text-xl font-bold text-gray-900 mb-2">{videoInfo.title}</h3>
1775
- </div>
1776
-
1777
- {/* Video Player */}
1778
- <div className="bg-black rounded-lg aspect-video overflow-hidden relative">
1779
- <video
1780
- className="w-full h-full"
1781
- controls
1782
- preload="metadata"
1783
- id="nike-video"
1784
- onError={(e) => console.error('Video loading error:', e)}
1785
- onLoadStart={() => console.log('Video loading started')}
1786
- onCanPlay={() => console.log('Video can play')}
1787
- >
1788
- <source src="https://huggingface.co/spaces/linguabot/transcreation-frontend/resolve/main/public/videos/nike-winning-isnt-for-everyone.mp4" type="video/mp4" />
1789
- Your browser does not support the video tag.
1790
- </video>
1791
-
1792
- {/* On-screen subtitles */}
1793
- {currentDisplayedSubtitle && (
1794
- <div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10">
1795
- <div className="bg-black bg-opacity-80 text-white px-6 py-3 rounded-lg text-center max-w-lg">
1796
- <p className={`text-base font-medium leading-relaxed tracking-wide ${currentDisplayedSubtitle.length <= 42 ? 'whitespace-nowrap' : 'whitespace-pre-line'}`} style={{ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
1797
- {formatSubtitleForDisplay(currentDisplayedSubtitle)}
1798
- </p>
1799
- </div>
1800
- </div>
1801
- )}
1802
- </div>
1803
- </div>
1804
-
1805
- {/* Right Panel - Translation Workspace */}
1806
  <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
1807
- <h3 className="text-xl font-bold text-gray-900 mb-4">Translation Workspace</h3>
1808
-
1809
- {/* Segment Navigation Grid */}
1810
- {loadingSubtitles ? (
1811
- <div className="grid grid-cols-9 gap-1 mb-6">
1812
- {Array.from({ length: 26 }, (_, i) => (
1813
- <div key={i} className="px-1 py-1 rounded text-xs w-full bg-gray-200 animate-pulse">
1814
- {i + 1}
1815
- </div>
1816
- ))}
1817
- </div>
1818
- ) : (
1819
- <div className="grid grid-cols-9 gap-1 mb-6">
1820
- {subtitleSegments.map((segment) => (
1821
- <button
1822
- key={segment.id}
1823
- onClick={() => handleSegmentClick(segment.id)}
1824
- className={`px-1 py-1 rounded text-xs w-full ${getSegmentButtonClass(segment.id)}`}
1825
- >
1826
- {segment.id}
1827
- </button>
1828
- ))}
1829
- </div>
1830
- )}
1831
-
1832
- {/* Admin Time Code Editor */}
1833
- {editingSegment && (((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin')) && (
1834
- <div className="bg-indigo-50 border border-indigo-200 rounded-lg p-4 mb-4">
1835
- <h4 className="text-sm font-medium text-indigo-800 mb-2">Edit Time Codes for Segment {editingSegment}</h4>
1836
- <div className="grid grid-cols-2 gap-2">
1837
- <div>
1838
- <label className="block text-xs text-indigo-700 mb-1">Start Time</label>
1839
- <input
1840
- type="text"
1841
- id="startTimeInput"
1842
- defaultValue={subtitleSegments[editingSegment - 1]?.startTime}
1843
- className="w-full px-2 py-1 text-xs border border-indigo-300 rounded"
1844
- placeholder="00:00:00,000"
1845
- />
1846
- </div>
1847
- <div>
1848
- <label className="block text-xs text-indigo-700 mb-1">End Time</label>
1849
- <input
1850
- type="text"
1851
- id="endTimeInput"
1852
- defaultValue={subtitleSegments[editingSegment - 1]?.endTime}
1853
- className="w-full px-2 py-1 text-xs border border-indigo-300 rounded"
1854
- placeholder="00:00:00,000"
1855
- />
1856
- </div>
1857
- </div>
1858
- <div className="flex space-x-2 mt-2">
1859
- <button
1860
- onClick={() => setEditingSegment(null)}
1861
- className="px-2 py-1 text-xs bg-gray-500 text-white rounded"
1862
- >
1863
- Cancel
1864
- </button>
1865
- <button
1866
- onClick={() => {
1867
- const startInput = document.getElementById('startTimeInput') as HTMLInputElement;
1868
- const endInput = document.getElementById('endTimeInput') as HTMLInputElement;
1869
- if (startInput && endInput && startInput.value && endInput.value) {
1870
- handleSaveTimeCode(editingSegment, startInput.value, endInput.value);
1871
- }
1872
- }}
1873
- className="px-2 py-1 text-xs bg-indigo-600 text-white rounded"
1874
- >
1875
- Save
1876
- </button>
1877
- </div>
1878
- </div>
1879
- )}
1880
-
1881
- {/* Current Segment Details */}
1882
- <div className="bg-gray-50 rounded-lg p-4 mb-4 border border-gray-200">
1883
- <div className="flex items-center justify-between">
1884
- <p className="text-gray-800">
1885
- Segment {currentSegment}: {subtitleSegments[currentSegment - 1]?.startTime} – {subtitleSegments[currentSegment - 1]?.endTime} ({subtitleSegments[currentSegment - 1]?.duration})
1886
- </p>
1887
- {((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
1888
- <button
1889
- onClick={() => handleEditTimeCode(currentSegment)}
1890
- className="bg-indigo-600 hover:bg-indigo-700 text-white px-2 py-1 rounded text-xs flex items-center space-x-1"
1891
- title="Edit time codes"
1892
- >
1893
- <PencilIcon className="w-3 h-3" />
1894
- <span>Edit</span>
1895
- </button>
1896
- )}
1897
- </div>
1898
- </div>
1899
-
1900
- {/* Source Text */}
1901
- <div className="mb-4">
1902
- <label className="block text-sm font-medium text-gray-700 mb-2">Source Text</label>
1903
- <textarea
1904
- value={subtitleText}
1905
- readOnly
1906
- className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-800"
1907
- rows={2}
1908
- />
1909
- </div>
1910
-
1911
- {/* Target Text */}
1912
- <div className="mb-4">
1913
- <label className="block text-sm font-medium text-gray-700 mb-2">Target Text</label>
1914
- <textarea
1915
- value={targetText}
1916
- onChange={(e) => handleTargetTextChange(e.target.value)}
1917
- className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
1918
- rows={2}
1919
- placeholder="Enter your translation..."
1920
- />
1921
- <div className="flex justify-between items-center mt-2">
1922
- <span className="text-xs text-gray-600">Recommended: 2 lines max, 16 chars/line (Netflix standard)</span>
1923
- <span className="text-xs text-gray-600">{characterCount} characters</span>
1924
- </div>
1925
- </div>
1926
-
1927
- {/* Action Buttons */}
1928
- <div className="flex space-x-3 mb-4">
1929
- <button
1930
- onClick={handleSaveTranslation}
1931
- className={`px-4 py-2 rounded-lg ${
1932
- saveStatus === 'Saved!' ? 'bg-green-600 hover:bg-green-700' :
1933
- saveStatus === 'Saved Locally' ? 'bg-yellow-600 hover:bg-yellow-700' :
1934
- saveStatus === 'Error' ? 'bg-red-600 hover:bg-red-700' :
1935
- 'bg-indigo-600 hover:bg-indigo-700'
1936
- } text-white`}
1937
- >
1938
- {saveStatus}
1939
- </button>
1940
- <button
1941
- onClick={handlePreviewTranslation}
1942
- className={`px-4 py-2 rounded-lg flex items-center space-x-2 ${
1943
- showTranslatedSubtitles
1944
- ? 'bg-green-500 hover:bg-green-600 text-white'
1945
- : 'bg-gray-500 hover:bg-gray-600 text-white'
1946
- }`}
1947
- >
1948
- <span>{showTranslatedSubtitles ? 'Show Original' : 'Show Translation'}</span>
1949
- </button>
1950
- </div>
1951
-
1952
- {/* Translation Progress */}
1953
- <div className="mb-4">
1954
- <div className="flex justify-between items-center mb-2">
1955
- <span className="text-sm font-medium text-gray-700">Translation Progress</span>
1956
- <span className="text-sm text-gray-600">{Object.keys(subtitleTranslations).length} of {subtitleSegments.length} segments completed</span>
1957
- </div>
1958
- <div className="w-full bg-gray-200 rounded-full h-2">
1959
- <div
1960
- className="bg-green-500 h-2 rounded-full transition-all duration-300"
1961
- style={{ width: `${(Object.keys(subtitleTranslations).length / subtitleSegments.length) * 100}%` }}
1962
- ></div>
1963
- </div>
1964
- </div>
1965
- </div>
1966
-
1967
- {/* Submissions Panel - Only show for Week 2 subtitling */}
1968
- - {selectedWeek === 2 && (
1969
- + {false && selectedWeek === 2 && (
1970
- <div className="mt-6 col-span-2">
1971
- <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
1972
- <div className="flex items-center justify-between mb-4">
1973
- <h3 className="text-lg font-semibold text-gray-900">
1974
- 👥 View Submissions for Segment {currentSegment}
1975
- </h3>
1976
- <button
1977
- onClick={toggleSubmissions}
1978
- className={`px-4 py-2 rounded-lg flex items-center space-x-2 transition-colors duration-200 ${
1979
- showSubmissions
1980
- ? 'bg-gray-500 hover:bg-gray-600 text-white'
1981
- : 'bg-purple-600 hover:bg-purple-700 text-white'
1982
- }`}
1983
- >
1984
- <span>{showSubmissions ? 'Hide Submissions' : 'Show Submissions'}</span>
1985
- <span>{showSubmissions ? '▼' : '▶'}</span>
1986
- </button>
1987
- </div>
1988
-
1989
- {showSubmissions && (
1990
- <div className="space-y-4">
1991
- {loadingSubmissions ? (
1992
- <div className="text-center py-8">
1993
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600 mx-auto mb-4"></div>
1994
- <p className="text-gray-600">Loading submissions...</p>
1995
- </div>
1996
- ) : submissions.length > 0 ? (
1997
- <>
1998
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
1999
- {submissions.map((submission) => (
2000
- <div key={submission._id} className="bg-gray-50 rounded-lg p-4 border border-gray-200">
2001
- <div className="flex items-center justify-between mb-2">
2002
- <span className="font-medium text-gray-900">
2003
- {submission.username}
2004
- </span>
2005
- <div className="flex items-center space-x-2">
2006
- <span className="text-xs text-gray-500">
2007
- {new Date(submission.submissionDate).toLocaleString()}
2008
- </span>
2009
- {((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
2010
- <button
2011
- className="px-2 py-1 text-xs bg-red-500 text-white rounded hover:bg-red-600"
2012
- onClick={async () => {
2013
- try {
2014
- await api.delete(`/api/subtitles/${submission._id}`);
2015
- await fetchUserSubmissions(weeklyPractice);
2016
- } catch (e) {
2017
- console.error('Delete subtitle submission failed', e);
2018
- }
2019
- }}
2020
- >
2021
- Delete
2022
- </button>
2023
- )}
2024
- </div>
2025
- </div>
2026
- <div className="text-sm text-gray-800 whitespace-pre-wrap">{submission.chineseTranslation}</div>
2027
- </div>
2028
- ))}
2029
- </div>
2030
- </>
2031
- ) : (
2032
- <div className="text-gray-600">No submissions yet.</div>
2033
- )}
2034
- </div>
2035
- )}
2036
- </div>
2037
- </div>
2038
- )}
2039
  </div>
2040
  ) : (
2041
  /* Weekly Practice */
 
1769
  </div>
1770
  ) : null}
1771
  {((localStorage.getItem('viewMode')||'auto') !== 'student' && (JSON.parse(localStorage.getItem('user')||'{}').role === 'admin') && showSubmissions) && (
1772
+ <>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1773
  <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
1774
+ <div className="mb-4">
1775
+ <h3 className="text-xl font-bold text-gray-900 mb-2">{videoInfo.title}</h3>
1776
+ </div>
1777
+
1778
+ {/* Video Player */}
1779
+ <div className="bg-black rounded-lg aspect-video overflow-hidden relative">
1780
+ <video
1781
+ className="w-full h-full"
1782
+ controls
1783
+ preload="metadata"
1784
+ id="nike-video"
1785
+ onError={(e) => console.error('Video loading error:', e)}
1786
+ onLoadStart={() => console.log('Video loading started')}
1787
+ onCanPlay={() => console.log('Video can play')}
1788
+ >
1789
+ <source src="https://huggingface.co/spaces/linguabot/transcreation-frontend/resolve/main/public/videos/nike-winning-isnt-for-everyone.mp4" type="video/mp4" />
1790
+ Your browser does not support the video tag.
1791
+ </video>
1792
+
1793
+ {/* On-screen subtitles */}
1794
+ {currentDisplayedSubtitle && (
1795
+ <div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10">
1796
+ <div className="bg-black bg-opacity-80 text-white px-6 py-3 rounded-lg text-center max-w-lg">
1797
+ <p className={`text-base font-medium leading-relaxed tracking-wide ${currentDisplayedSubtitle.length <= 42 ? 'whitespace-nowrap' : 'whitespace-pre-line'}`} style={{ fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
1798
+ {formatSubtitleForDisplay(currentDisplayedSubtitle)}
1799
+ </p>
1800
+ </div>
1801
+ </div>
1802
+ )}
1803
+ </div>
1804
+ </div>
1805
+
1806
+ {/* Right Panel - Translation Workspace */}
1807
+ <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6">
1808
+ <h3 className="text-xl font-bold text-gray-900 mb-4">Translation Workspace</h3>
1809
+
1810
+ {/* Segment Navigation Grid */}
1811
+ {loadingSubtitles ? (
1812
+ <div className="grid grid-cols-9 gap-1 mb-6">
1813
+ {Array.from({ length: 26 }, (_, i) => (
1814
+ <div key={i} className="px-1 py-1 rounded text-xs w-full bg-gray-200 animate-pulse">
1815
+ {i + 1}
1816
+ </div>
1817
+ ))}
1818
+ </div>
1819
+ ) : (
1820
+ <div className="grid grid-cols-9 gap-1 mb-6">
1821
+ {subtitleSegments.map((segment) => (
1822
+ <button
1823
+ key={segment.id}
1824
+ onClick={() => handleSegmentClick(segment.id)}
1825
+ className={`px-1 py-1 rounded text-xs w-full ${getSegmentButtonClass(segment.id)}`}
1826
+ >
1827
+ {segment.id}
1828
+ </button>
1829
+ ))}
1830
+ </div>
1831
+ )}
1832
+
1833
+ {/* Admin Time Code Editor */}
1834
+ {editingSegment && (((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin')) && (
1835
+ <div className="bg-indigo-50 border border-indigo-200 rounded-lg p-4 mb-4">
1836
+ <h4 className="text-sm font-medium text-indigo-800 mb-2">Edit Time Codes for Segment {editingSegment}</h4>
1837
+ <div className="grid grid-cols-2 gap-2">
1838
+ <div>
1839
+ <label className="block text-xs text-indigo-700 mb-1">Start Time</label>
1840
+ <input
1841
+ type="text"
1842
+ id="startTimeInput"
1843
+ defaultValue={subtitleSegments[editingSegment - 1]?.startTime}
1844
+ className="w-full px-2 py-1 text-xs border border-indigo-300 rounded"
1845
+ placeholder="00:00:00,000"
1846
+ />
1847
+ </div>
1848
+ <div>
1849
+ <label className="block text-xs text-indigo-700 mb-1">End Time</label>
1850
+ <input
1851
+ type="text"
1852
+ id="endTimeInput"
1853
+ defaultValue={subtitleSegments[editingSegment - 1]?.endTime}
1854
+ className="w-full px-2 py-1 text-xs border border-indigo-300 rounded"
1855
+ placeholder="00:00:00,000"
1856
+ />
1857
+ </div>
1858
+ </div>
1859
+ <div className="flex space-x-2 mt-2">
1860
+ <button
1861
+ onClick={() => setEditingSegment(null)}
1862
+ className="px-2 py-1 text-xs bg-gray-500 text-white rounded"
1863
+ >
1864
+ Cancel
1865
+ </button>
1866
+ <button
1867
+ onClick={() => {
1868
+ const startInput = document.getElementById('startTimeInput') as HTMLInputElement;
1869
+ const endInput = document.getElementById('endTimeInput') as HTMLInputElement;
1870
+ if (startInput && endInput && startInput.value && endInput.value) {
1871
+ handleSaveTimeCode(editingSegment, startInput.value, endInput.value);
1872
+ }
1873
+ }}
1874
+ className="px-2 py-1 text-xs bg-indigo-600 text-white rounded"
1875
+ >
1876
+ Save
1877
+ </button>
1878
+ </div>
1879
+ </div>
1880
+ )}
1881
+
1882
+ {/* Current Segment Details */}
1883
+ <div className="bg-gray-50 rounded-lg p-4 mb-4 border border-gray-200">
1884
+ <div className="flex items-center justify-between">
1885
+ <p className="text-gray-800">
1886
+ Segment {currentSegment}: {subtitleSegments[currentSegment - 1]?.startTime} – {subtitleSegments[currentSegment - 1]?.endTime} ({subtitleSegments[currentSegment - 1]?.duration})
1887
+ </p>
1888
+ {((localStorage.getItem('viewMode')||'auto') === 'student') ? false : (JSON.parse(localStorage.getItem('user') || '{}').role === 'admin') && (
1889
+ <button
1890
+ onClick={() => handleEditTimeCode(currentSegment)}
1891
+ className="bg-indigo-600 hover:bg-indigo-700 text-white px-2 py-1 rounded text-xs flex items-center space-x-1"
1892
+ title="Edit time codes"
1893
+ >
1894
+ <PencilIcon className="w-3 h-3" />
1895
+ <span>Edit</span>
1896
+ </button>
1897
+ )}
1898
+ </div>
1899
+ </div>
1900
+
1901
+ {/* Source Text */}
1902
+ <div className="mb-4">
1903
+ <label className="block text-sm font-medium text-gray-700 mb-2">Source Text</label>
1904
+ <textarea
1905
+ value={subtitleText}
1906
+ readOnly
1907
+ className="w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-800"
1908
+ rows={2}
1909
+ />
1910
+ </div>
1911
+
1912
+ {/* Target Text */}
1913
+ <div className="mb-4">
1914
+ <label className="block text-sm font-medium text-gray-700 mb-2">Target Text</label>
1915
+ <textarea
1916
+ value={targetText}
1917
+ onChange={(e) => handleTargetTextChange(e.target.value)}
1918
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
1919
+ rows={2}
1920
+ placeholder="Enter your translation..."
1921
+ />
1922
+ <div className="flex justify-between items-center mt-2">
1923
+ <span className="text-xs text-gray-600">Recommended: 2 lines max, 16 chars/line (Netflix standard)</span>
1924
+ <span className="text-xs text-gray-600">{characterCount} characters</span>
1925
+ </div>
1926
+ </div>
1927
+
1928
+ {/* Action Buttons */}
1929
+ <div className="flex space-x-3 mb-4">
1930
+ <button
1931
+ onClick={handleSaveTranslation}
1932
+ className={`px-4 py-2 rounded-lg ${
1933
+ saveStatus === 'Saved!' ? 'bg-green-600 hover:bg-green-700' :
1934
+ saveStatus === 'Saved Locally' ? 'bg-yellow-600 hover:bg-yellow-700' :
1935
+ saveStatus === 'Error' ? 'bg-red-600 hover:bg-red-700' :
1936
+ 'bg-indigo-600 hover:bg-indigo-700'
1937
+ } text-white`}
1938
+ >
1939
+ {saveStatus}
1940
+ </button>
1941
+ <button
1942
+ onClick={handlePreviewTranslation}
1943
+ className={`px-4 py-2 rounded-lg flex items-center space-x-2 ${
1944
+ showTranslatedSubtitles
1945
+ ? 'bg-green-500 hover:bg-green-600 text-white'
1946
+ : 'bg-gray-500 hover:bg-gray-600 text-white'
1947
+ }`}
1948
+ >
1949
+ <span>{showTranslatedSubtitles ? 'Show Original' : 'Show Translation'}</span>
1950
+ </button>
1951
+ </div>
1952
+
1953
+ {/* Translation Progress */}
1954
+ <div className="mb-4">
1955
+ <div className="flex justify-between items-center mb-2">
1956
+ <span className="text-sm font-medium text-gray-700">Translation Progress</span>
1957
+ <span className="text-sm text-gray-600">{Object.keys(subtitleTranslations).length} of {subtitleSegments.length} segments completed</span>
1958
+ </div>
1959
+ <div className="w-full bg-gray-200 rounded-full h-2">
1960
+ <div
1961
+ className="bg-green-500 h-2 rounded-full transition-all duration-300"
1962
+ style={{ width: `${(Object.keys(subtitleTranslations).length / subtitleSegments.length) * 100}%` }}
1963
+ ></div>
1964
+ </div>
1965
+ </div>
1966
+ </div>
1967
+ </>
1968
+ )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1969
  </div>
1970
  ) : (
1971
  /* Weekly Practice */