Spaces:
Sleeping
Sleeping
Tristan Yu commited on
Commit ·
2c94120
1
Parent(s): 04f8035
Fix layout logic: Add Task uses side-by-side for ALL weeks, Add Image uses dynamic layout, fix WeeklyPractice image display
Browse files
client/src/pages/TutorialTasks.tsx
CHANGED
|
@@ -1398,9 +1398,9 @@ const TutorialTasks: React.FC = () => {
|
|
| 1398 |
) : (
|
| 1399 |
<div className="space-y-4">
|
| 1400 |
{task.imageUrl ? (
|
| 1401 |
-
//
|
| 1402 |
-
task.
|
| 1403 |
-
//
|
| 1404 |
<div className={`flex flex-col md:flex-row gap-6 items-start ${
|
| 1405 |
task.imageAlignment === 'left' ? 'md:flex-row' :
|
| 1406 |
task.imageAlignment === 'right' ? 'md:flex-row-reverse' :
|
|
@@ -1436,19 +1436,9 @@ const TutorialTasks: React.FC = () => {
|
|
| 1436 |
)}
|
| 1437 |
</div>
|
| 1438 |
</div>
|
| 1439 |
-
{/* Text section - only show if content exists and is not image-only */}
|
| 1440 |
-
{task.content && task.content !== 'Image-based task' && (
|
| 1441 |
-
<div className={`${
|
| 1442 |
-
task.imageAlignment === 'left' ? 'w-full md:w-1/2' :
|
| 1443 |
-
task.imageAlignment === 'right' ? 'w-full md:w-1/2' :
|
| 1444 |
-
'w-full'
|
| 1445 |
-
}`}>
|
| 1446 |
-
<div className="text-indigo-900 leading-relaxed text-lg font-source-text whitespace-pre-wrap">{task.content}</div>
|
| 1447 |
-
</div>
|
| 1448 |
-
)}
|
| 1449 |
</div>
|
| 1450 |
) : (
|
| 1451 |
-
//
|
| 1452 |
<div className="flex flex-col md:flex-row gap-6 items-start">
|
| 1453 |
{/* Image on the left - 50% width */}
|
| 1454 |
<div className="w-full md:w-1/2 flex justify-center">
|
|
@@ -1576,8 +1566,8 @@ const TutorialTasks: React.FC = () => {
|
|
| 1576 |
</div>
|
| 1577 |
)}
|
| 1578 |
|
| 1579 |
-
{/* Translation Input (always show if user is logged in, but hide for image-only content
|
| 1580 |
-
{localStorage.getItem('token') &&
|
| 1581 |
<div className="bg-white rounded-lg p-6 border border-gray-200 shadow-sm">
|
| 1582 |
<div className="flex items-center space-x-3 mb-4">
|
| 1583 |
<div className="bg-gray-100 rounded-lg p-2">
|
|
|
|
| 1398 |
) : (
|
| 1399 |
<div className="space-y-4">
|
| 1400 |
{task.imageUrl ? (
|
| 1401 |
+
// Check if this is an image-only task (created via "Add Image" function)
|
| 1402 |
+
task.content === 'Image-based task' ? (
|
| 1403 |
+
// Image-only layout with dynamic sizing and alignment
|
| 1404 |
<div className={`flex flex-col md:flex-row gap-6 items-start ${
|
| 1405 |
task.imageAlignment === 'left' ? 'md:flex-row' :
|
| 1406 |
task.imageAlignment === 'right' ? 'md:flex-row-reverse' :
|
|
|
|
| 1436 |
)}
|
| 1437 |
</div>
|
| 1438 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1439 |
</div>
|
| 1440 |
) : (
|
| 1441 |
+
// Regular task layout (original side-by-side for ALL weeks)
|
| 1442 |
<div className="flex flex-col md:flex-row gap-6 items-start">
|
| 1443 |
{/* Image on the left - 50% width */}
|
| 1444 |
<div className="w-full md:w-1/2 flex justify-center">
|
|
|
|
| 1566 |
</div>
|
| 1567 |
)}
|
| 1568 |
|
| 1569 |
+
{/* Translation Input (always show if user is logged in, but hide for image-only content) */}
|
| 1570 |
+
{localStorage.getItem('token') && task.content !== 'Image-based task' && (
|
| 1571 |
<div className="bg-white rounded-lg p-6 border border-gray-200 shadow-sm">
|
| 1572 |
<div className="flex items-center space-x-3 mb-4">
|
| 1573 |
<div className="bg-gray-100 rounded-lg p-2">
|
client/src/pages/WeeklyPractice.tsx
CHANGED
|
@@ -2030,43 +2030,25 @@ const WeeklyPractice: React.FC = () => {
|
|
| 2030 |
</div>
|
| 2031 |
) : (
|
| 2032 |
<div>
|
| 2033 |
-
{practice.imageUrl
|
| 2034 |
-
|
| 2035 |
-
|
| 2036 |
-
|
| 2037 |
-
|
| 2038 |
-
|
| 2039 |
-
|
| 2040 |
-
|
| 2041 |
-
practice.imageAlignment === 'left' ? 'w-full md:w-1/2' :
|
| 2042 |
-
practice.imageAlignment === 'right' ? 'w-full md:w-1/2' :
|
| 2043 |
-
'w-full'
|
| 2044 |
-
} flex ${
|
| 2045 |
-
practice.imageAlignment === 'left' ? 'justify-start' :
|
| 2046 |
-
practice.imageAlignment === 'right' ? 'justify-end' :
|
| 2047 |
-
'justify-center'
|
| 2048 |
}`}>
|
| 2049 |
-
{
|
| 2050 |
-
|
| 2051 |
-
|
| 2052 |
-
|
| 2053 |
-
|
| 2054 |
-
|
| 2055 |
-
|
| 2056 |
-
|
| 2057 |
-
|
| 2058 |
-
|
| 2059 |
-
}}
|
| 2060 |
-
onError={(e) => {
|
| 2061 |
-
console.error('Error loading image:', e);
|
| 2062 |
-
e.currentTarget.style.display = 'none';
|
| 2063 |
-
}}
|
| 2064 |
-
/>
|
| 2065 |
-
{practice.imageAlt && (
|
| 2066 |
-
<div className="text-xs text-gray-500 mt-2 text-center">Alt: {practice.imageAlt}</div>
|
| 2067 |
-
)}
|
| 2068 |
-
</div>
|
| 2069 |
-
) : (
|
| 2070 |
<div className="inline-block rounded-lg shadow-md overflow-hidden">
|
| 2071 |
<img
|
| 2072 |
src={practice.imageUrl}
|
|
@@ -2080,38 +2062,50 @@ const WeeklyPractice: React.FC = () => {
|
|
| 2080 |
onError={(e) => {
|
| 2081 |
console.error('Error loading image:', e);
|
| 2082 |
e.currentTarget.style.display = 'none';
|
| 2083 |
-
// Show fallback if image fails to load
|
| 2084 |
-
const fallback = e.currentTarget.parentElement;
|
| 2085 |
-
if (fallback) {
|
| 2086 |
-
fallback.innerHTML = `
|
| 2087 |
-
<div class="inline-block rounded-lg shadow-md bg-green-500 text-white p-6 text-center">
|
| 2088 |
-
<div class="text-3xl mb-2">📷</div>
|
| 2089 |
-
<div class="font-semibold">Image Uploaded</div>
|
| 2090 |
-
<div class="text-sm opacity-75">${practice.imageUrl}</div>
|
| 2091 |
-
${practice.imageAlt ? `<div class="text-xs opacity-50 mt-2">Alt: ${practice.imageAlt}</div>` : ''}
|
| 2092 |
-
</div>
|
| 2093 |
-
`;
|
| 2094 |
-
}
|
| 2095 |
}}
|
| 2096 |
/>
|
| 2097 |
{practice.imageAlt && (
|
| 2098 |
<div className="text-xs text-gray-500 mt-2 text-center">Alt: {practice.imageAlt}</div>
|
| 2099 |
)}
|
| 2100 |
</div>
|
| 2101 |
-
|
| 2102 |
</div>
|
| 2103 |
-
|
| 2104 |
-
|
| 2105 |
-
|
| 2106 |
-
|
| 2107 |
-
|
| 2108 |
-
'
|
| 2109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2110 |
<p className="text-orange-800 leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</p>
|
| 2111 |
</div>
|
| 2112 |
-
|
| 2113 |
-
|
| 2114 |
) : (
|
|
|
|
| 2115 |
<p className="text-orange-800 leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</p>
|
| 2116 |
)}
|
| 2117 |
</div>
|
|
@@ -2198,8 +2192,8 @@ const WeeklyPractice: React.FC = () => {
|
|
| 2198 |
</div>
|
| 2199 |
)}
|
| 2200 |
|
| 2201 |
-
{/* Translation Input (only show if user is logged in and has no submission,
|
| 2202 |
-
{localStorage.getItem('token') && (!userSubmissions[practice._id] || userSubmissions[practice._id].length === 0) &&
|
| 2203 |
<div className="bg-gradient-to-r from-purple-50 to-violet-50 rounded-xl p-6 border border-purple-200">
|
| 2204 |
<div className="flex items-center space-x-2 mb-4">
|
| 2205 |
<div className="bg-purple-100 rounded-full p-1">
|
|
|
|
| 2030 |
</div>
|
| 2031 |
) : (
|
| 2032 |
<div>
|
| 2033 |
+
{practice.imageUrl ? (
|
| 2034 |
+
// Check if this is an image-only practice (created via "Add Image" function)
|
| 2035 |
+
practice.content === 'Image-based practice' ? (
|
| 2036 |
+
// Image-only layout with dynamic sizing and alignment
|
| 2037 |
+
<div className={`flex flex-col md:flex-row gap-6 items-start ${
|
| 2038 |
+
practice.imageAlignment === 'left' ? 'md:flex-row' :
|
| 2039 |
+
practice.imageAlignment === 'right' ? 'md:flex-row-reverse' :
|
| 2040 |
+
'md:flex-col'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2041 |
}`}>
|
| 2042 |
+
{/* Image section */}
|
| 2043 |
+
<div className={`${
|
| 2044 |
+
practice.imageAlignment === 'left' ? 'w-full md:w-1/2' :
|
| 2045 |
+
practice.imageAlignment === 'right' ? 'w-full md:w-1/2' :
|
| 2046 |
+
'w-full'
|
| 2047 |
+
} flex ${
|
| 2048 |
+
practice.imageAlignment === 'left' ? 'justify-start' :
|
| 2049 |
+
practice.imageAlignment === 'right' ? 'justify-end' :
|
| 2050 |
+
'justify-center'
|
| 2051 |
+
}`}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2052 |
<div className="inline-block rounded-lg shadow-md overflow-hidden">
|
| 2053 |
<img
|
| 2054 |
src={practice.imageUrl}
|
|
|
|
| 2062 |
onError={(e) => {
|
| 2063 |
console.error('Error loading image:', e);
|
| 2064 |
e.currentTarget.style.display = 'none';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2065 |
}}
|
| 2066 |
/>
|
| 2067 |
{practice.imageAlt && (
|
| 2068 |
<div className="text-xs text-gray-500 mt-2 text-center">Alt: {practice.imageAlt}</div>
|
| 2069 |
)}
|
| 2070 |
</div>
|
| 2071 |
+
</div>
|
| 2072 |
</div>
|
| 2073 |
+
) : (
|
| 2074 |
+
// Regular practice layout (original side-by-side for ALL weeks)
|
| 2075 |
+
<div className="flex flex-col md:flex-row gap-6 items-start">
|
| 2076 |
+
{/* Image on the left - 50% width */}
|
| 2077 |
+
<div className="w-full md:w-1/2 flex justify-center">
|
| 2078 |
+
{practice.imageUrl.startsWith('data:') ? (
|
| 2079 |
+
// Show actual image if it's a data URL
|
| 2080 |
+
<div className="inline-block rounded-lg shadow-md overflow-hidden">
|
| 2081 |
+
<img
|
| 2082 |
+
src={practice.imageUrl}
|
| 2083 |
+
alt={practice.imageAlt || 'Uploaded image'}
|
| 2084 |
+
className="w-full h-auto"
|
| 2085 |
+
style={{ height: '200px', width: 'auto', objectFit: 'contain' }} // Fixed height for consistency
|
| 2086 |
+
onError={(e) => {
|
| 2087 |
+
console.error('Error loading image:', e);
|
| 2088 |
+
e.currentTarget.style.display = 'none';
|
| 2089 |
+
}}
|
| 2090 |
+
/>
|
| 2091 |
+
</div>
|
| 2092 |
+
) : (
|
| 2093 |
+
// Show placeholder if it's not a data URL
|
| 2094 |
+
<div className="inline-block rounded-lg shadow-md bg-green-500 text-white p-6 text-center">
|
| 2095 |
+
<div className="text-3xl mb-2">📷</div>
|
| 2096 |
+
<div className="font-semibold">Image Uploaded</div>
|
| 2097 |
+
<div className="text-sm opacity-75">{practice.imageUrl}</div>
|
| 2098 |
+
</div>
|
| 2099 |
+
)}
|
| 2100 |
+
</div>
|
| 2101 |
+
{/* Text on the right - 50% width */}
|
| 2102 |
+
<div className="w-full md:w-1/2">
|
| 2103 |
<p className="text-orange-800 leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</p>
|
| 2104 |
</div>
|
| 2105 |
+
</div>
|
| 2106 |
+
)
|
| 2107 |
) : (
|
| 2108 |
+
// Text only when no image
|
| 2109 |
<p className="text-orange-800 leading-relaxed text-lg font-source-text whitespace-pre-wrap">{practice.content}</p>
|
| 2110 |
)}
|
| 2111 |
</div>
|
|
|
|
| 2192 |
</div>
|
| 2193 |
)}
|
| 2194 |
|
| 2195 |
+
{/* Translation Input (only show if user is logged in and has no submission, but hide for image-only content) */}
|
| 2196 |
+
{localStorage.getItem('token') && (!userSubmissions[practice._id] || userSubmissions[practice._id].length === 0) && practice.content !== 'Image-based practice' && (
|
| 2197 |
<div className="bg-gradient-to-r from-purple-50 to-violet-50 rounded-xl p-6 border border-purple-200">
|
| 2198 |
<div className="flex items-center space-x-2 mb-4">
|
| 2199 |
<div className="bg-purple-100 rounded-full p-1">
|