linguabot commited on
Commit
1aa7dd8
·
verified ·
1 Parent(s): 1c691f7

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. client/src/pages/TutorialTasks.tsx +50 -8
client/src/pages/TutorialTasks.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useCallback } from 'react';
2
  import { useNavigate } from 'react-router-dom';
3
  import { api } from '../services/api';
4
  import {
@@ -63,6 +63,14 @@ const TutorialTasks: React.FC = () => {
63
  const [tutorialWeek, setTutorialWeek] = useState<TutorialWeek | null>(null);
64
  const [userSubmissions, setUserSubmissions] = useState<{[key: string]: UserSubmission[]}>({});
65
  const [sourceHeights, setSourceHeights] = useState<{[key: string]: number}>({});
 
 
 
 
 
 
 
 
66
 
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') => {
@@ -111,8 +119,7 @@ const TutorialTasks: React.FC = () => {
111
  console.error('Reorder failed', error);
112
  }
113
  };
114
- const [loading, setLoading] = useState(true);
115
- const [submitting, setSubmitting] = useState<{[key: string]: boolean}>({});
116
  const [isWeekHidden, setIsWeekHidden] = useState<boolean>(false);
117
  const [translationText, setTranslationText] = useState<{[key: string]: string}>({});
118
  const [selectedGroups, setSelectedGroups] = useState<{[key: string]: number}>({});
@@ -1106,6 +1113,25 @@ const TutorialTasks: React.FC = () => {
1106
 
1107
  // Remove intrusive loading screen - just show content with loading state
1108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
  return (
1110
  <div className="min-h-screen bg-white py-8">
1111
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -1640,7 +1666,12 @@ const TutorialTasks: React.FC = () => {
1640
  </div>
1641
  ) : (
1642
  tutorialTasks.map((task) => (
1643
- <div key={task._id} className="bg-white rounded-xl shadow-lg border border-gray-100 p-8 hover:shadow-xl transition-shadow duration-300">
 
 
 
 
 
1644
  <div className="mb-6">
1645
  <div className="flex items-center justify-between mb-4">
1646
  <div className="flex items-center space-x-3">
@@ -1852,7 +1883,9 @@ const TutorialTasks: React.FC = () => {
1852
  <label className="block text-xs font-medium text-gray-700 mb-1">Select Your Group</label>
1853
  <select
1854
  value={selectedGroups[task._id] || ''}
1855
- onChange={(e) => setSelectedGroups({ ...selectedGroups, [task._id]: parseInt(e.target.value) })}
 
 
1856
  className="w-40 px-2 py-1 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white text-xs"
1857
  >
1858
  <option value="">Choose...</option>
@@ -1867,14 +1900,17 @@ const TutorialTasks: React.FC = () => {
1867
  <textarea
1868
  id={`tutorial-translation-${task._id}`}
1869
  value={translationText[task._id] || ''}
 
 
1870
  onChange={(e) => setTranslationText({ ...translationText, [task._id]: e.target.value })}
 
1871
  className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white"
1872
  style={{ height: sourceHeights[task._id] ? `${sourceHeights[task._id]}px` : 'auto' }}
1873
  rows={4}
1874
  placeholder="Enter your group's translation here..."
1875
  />
1876
  <div className="flex justify-end mt-2">
1877
- <button onClick={() => handleSubmitTranslation(task._id)} disabled={submitting[task._id]} className="btn-primary disabled:bg-gray-400 text-white px-4 py-2 rounded-lg text-sm">{submitting[task._id] ? 'Submitting...' : 'Submit Translation'}</button>
1878
  </div>
1879
  </div>
1880
  )}
@@ -2037,7 +2073,9 @@ const TutorialTasks: React.FC = () => {
2037
  </label>
2038
  <select
2039
  value={selectedGroups[task._id] || ''}
2040
- onChange={(e) => setSelectedGroups({ ...selectedGroups, [task._id]: parseInt(e.target.value) })}
 
 
2041
  className="w-48 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white text-sm"
2042
  required
2043
  >
@@ -2062,15 +2100,19 @@ const TutorialTasks: React.FC = () => {
2062
  <textarea
2063
  id={`tutorial-translation-${task._id}`}
2064
  value={translationText[task._id] || ''}
 
 
2065
  onChange={(e) => setTranslationText({ ...translationText, [task._id]: e.target.value })}
 
2066
  className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white"
 
2067
  rows={4}
2068
  placeholder="Enter your group's translation here..."
2069
  />
2070
  </div>
2071
 
2072
  <button
2073
- onClick={() => handleSubmitTranslation(task._id)}
2074
  disabled={submitting[task._id]}
2075
  className="relative inline-flex items-center justify-center gap-2 px-4 py-2 rounded-2xl text-sm font-medium text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 shadow-[inset_0_1px_0_rgba(255,255,255,0.6),inset_0_-1px_0_rgba(0,0,0,0.12)] bg-sky-600/70 disabled:bg-gray-400"
2076
  >
 
1
+ import React, { useState, useEffect, useCallback, useRef } from 'react';
2
  import { useNavigate } from 'react-router-dom';
3
  import { api } from '../services/api';
4
  import {
 
63
  const [tutorialWeek, setTutorialWeek] = useState<TutorialWeek | null>(null);
64
  const [userSubmissions, setUserSubmissions] = useState<{[key: string]: UserSubmission[]}>({});
65
  const [sourceHeights, setSourceHeights] = useState<{[key: string]: number}>({});
66
+ const [loading, setLoading] = useState(true);
67
+ const [submitting, setSubmitting] = useState<{[key: string]: boolean}>({});
68
+ const [isWeekHidden, setIsWeekHidden] = useState<boolean>(false);
69
+ const [translationText, setTranslationText] = useState<{[key: string]: string}>({});
70
+ const [selectedGroups, setSelectedGroups] = useState<{[key: string]: number}>({});
71
+ const [expandedSections, setExpandedSections] = useState<{[key: string]: boolean}>({});
72
+ const [lockedCardHeights, setLockedCardHeights] = useState<{[key: string]: number}>({});
73
+ const cardRefs = useRef<{[key: string]: HTMLDivElement | null}>({});
74
 
75
  // Move a task up or down by normalizing positions for the current visible list (weeks 4–6 only)
76
  const moveTask = async (taskId: string, direction: 'up' | 'down') => {
 
119
  console.error('Reorder failed', error);
120
  }
121
  };
122
+
 
123
  const [isWeekHidden, setIsWeekHidden] = useState<boolean>(false);
124
  const [translationText, setTranslationText] = useState<{[key: string]: string}>({});
125
  const [selectedGroups, setSelectedGroups] = useState<{[key: string]: number}>({});
 
1113
 
1114
  // Remove intrusive loading screen - just show content with loading state
1115
 
1116
+ const lockCardHeight = (taskId: string) => {
1117
+ try {
1118
+ const el = cardRefs.current[taskId];
1119
+ if (!el) return;
1120
+ const h = el.getBoundingClientRect().height;
1121
+ if (h && h > 0) {
1122
+ setLockedCardHeights(prev => ({ ...prev, [taskId]: Math.ceil(h) }));
1123
+ }
1124
+ } catch {}
1125
+ };
1126
+
1127
+ const unlockCardHeight = (taskId: string) => {
1128
+ setLockedCardHeights(prev => {
1129
+ const next = { ...prev } as any;
1130
+ delete next[taskId];
1131
+ return next;
1132
+ });
1133
+ };
1134
+
1135
  return (
1136
  <div className="min-h-screen bg-white py-8">
1137
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
 
1666
  </div>
1667
  ) : (
1668
  tutorialTasks.map((task) => (
1669
+ <div
1670
+ key={task._id}
1671
+ ref={(el) => { (cardRefs.current[task._id] = el); }}
1672
+ className="bg-white rounded-xl shadow-lg border border-gray-100 p-8 hover:shadow-xl transition-shadow duration-300"
1673
+ style={lockedCardHeights[task._id] ? { height: lockedCardHeights[task._id], overflow: 'hidden' } : undefined}
1674
+ >
1675
  <div className="mb-6">
1676
  <div className="flex items-center justify-between mb-4">
1677
  <div className="flex items-center space-x-3">
 
1883
  <label className="block text-xs font-medium text-gray-700 mb-1">Select Your Group</label>
1884
  <select
1885
  value={selectedGroups[task._id] || ''}
1886
+ onFocus={() => lockCardHeight(task._id)}
1887
+ onChange={(e) => { lockCardHeight(task._id); setSelectedGroups({ ...selectedGroups, [task._id]: parseInt(e.target.value) }); }}
1888
+ onBlur={() => unlockCardHeight(task._id)}
1889
  className="w-40 px-2 py-1 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white text-xs"
1890
  >
1891
  <option value="">Choose...</option>
 
1900
  <textarea
1901
  id={`tutorial-translation-${task._id}`}
1902
  value={translationText[task._id] || ''}
1903
+ onFocus={() => lockCardHeight(task._id)}
1904
+ onInput={() => lockCardHeight(task._id)}
1905
  onChange={(e) => setTranslationText({ ...translationText, [task._id]: e.target.value })}
1906
+ onBlur={() => unlockCardHeight(task._id)}
1907
  className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white"
1908
  style={{ height: sourceHeights[task._id] ? `${sourceHeights[task._id]}px` : 'auto' }}
1909
  rows={4}
1910
  placeholder="Enter your group's translation here..."
1911
  />
1912
  <div className="flex justify-end mt-2">
1913
+ <button onClick={() => { lockCardHeight(task._id); handleSubmitTranslation(task._id).finally(() => setTimeout(() => unlockCardHeight(task._id), 300)); }} disabled={submitting[task._id]} className="btn-primary disabled:bg-gray-400 text-white px-4 py-2 rounded-lg text-sm">{submitting[task._id] ? 'Submitting...' : 'Submit Translation'}</button>
1914
  </div>
1915
  </div>
1916
  )}
 
2073
  </label>
2074
  <select
2075
  value={selectedGroups[task._id] || ''}
2076
+ onFocus={() => lockCardHeight(task._id)}
2077
+ onChange={(e) => { lockCardHeight(task._id); setSelectedGroups({ ...selectedGroups, [task._id]: parseInt(e.target.value) }); }}
2078
+ onBlur={() => unlockCardHeight(task._id)}
2079
  className="w-48 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white text-sm"
2080
  required
2081
  >
 
2100
  <textarea
2101
  id={`tutorial-translation-${task._id}`}
2102
  value={translationText[task._id] || ''}
2103
+ onFocus={() => lockCardHeight(task._id)}
2104
+ onInput={() => lockCardHeight(task._id)}
2105
  onChange={(e) => setTranslationText({ ...translationText, [task._id]: e.target.value })}
2106
+ onBlur={() => unlockCardHeight(task._id)}
2107
  className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 bg-white"
2108
+ style={{ height: sourceHeights[task._id] ? `${sourceHeights[task._id]}px` : 'auto' }}
2109
  rows={4}
2110
  placeholder="Enter your group's translation here..."
2111
  />
2112
  </div>
2113
 
2114
  <button
2115
+ onClick={() => { lockCardHeight(task._id); handleSubmitTranslation(task._id).finally(() => setTimeout(() => unlockCardHeight(task._id), 300)); }}
2116
  disabled={submitting[task._id]}
2117
  className="relative inline-flex items-center justify-center gap-2 px-4 py-2 rounded-2xl text-sm font-medium text-white ring-1 ring-inset ring-white/50 backdrop-blur-md backdrop-brightness-110 backdrop-saturate-150 shadow-[inset_0_1px_0_rgba(255,255,255,0.6),inset_0_-1px_0_rgba(0,0,0,0.12)] bg-sky-600/70 disabled:bg-gray-400"
2118
  >