LinkDesk/frontend/test-tasks-store-optimizati...

450 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TasksStore Optimization Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.test-section {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.success { background-color: #d4edda; border-color: #c3e6cb; }
.error { background-color: #f8d7da; border-color: #f5c6cb; }
.info { background-color: #d1ecf1; border-color: #bee5eb; }
pre {
background-color: #f8f9fa;
padding: 10px;
border-radius: 3px;
overflow-x: auto;
}
button {
background-color: #007bff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:hover {
background-color: #0056b3;
}
.task-item {
padding: 8px;
margin: 4px 0;
background-color: #f8f9fa;
border-left: 4px solid #007bff;
}
</style>
</head>
<body>
<h1>TasksStore Optimization Test</h1>
<p>Testing the optimized TasksStore that uses embedded task data from shots/assets.</p>
<div class="test-section info">
<h3>Test Configuration</h3>
<p>This test verifies that the TasksStore correctly:</p>
<ul>
<li>Uses embedded task data from shots and assets when projectId is provided</li>
<li>Combines task data from both shots and assets into a single array</li>
<li>Maintains existing store interface and computed properties</li>
<li>Applies filters correctly to the combined task data</li>
</ul>
</div>
<div class="test-section">
<h3>Test Controls</h3>
<button onclick="testOptimizedFetch()">Test Optimized Fetch (with projectId)</button>
<button onclick="testLegacyFetch()">Test Legacy Fetch (without projectId)</button>
<button onclick="testFiltering()">Test Task Filtering</button>
<button onclick="testComputedProperties()">Test Computed Properties</button>
<button onclick="clearResults()">Clear Results</button>
</div>
<div id="results"></div>
<script type="module">
// Mock the services for testing
const mockShotService = {
getShotsByProject: async (projectId) => {
console.log('Mock: Fetching shots for project', projectId);
return [
{
id: 1,
name: 'SH010',
project_id: projectId,
project_name: 'Test Project',
episode_id: 1,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
task_details: [
{
task_id: 101,
task_type: 'layout',
status: 'in_progress',
assigned_user_id: 1
},
{
task_id: 102,
task_type: 'animation',
status: 'not_started',
assigned_user_id: null
}
]
},
{
id: 2,
name: 'SH020',
project_id: projectId,
project_name: 'Test Project',
episode_id: 1,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
task_details: [
{
task_id: 103,
task_type: 'layout',
status: 'approved',
assigned_user_id: 2
}
]
}
];
}
};
const mockAssetService = {
getAssets: async (projectId) => {
console.log('Mock: Fetching assets for project', projectId);
return [
{
id: 1,
name: 'Character_Hero',
project_id: projectId,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
task_details: [
{
task_id: 201,
task_type: 'modeling',
status: 'approved',
assigned_user_id: 1
},
{
task_id: 202,
task_type: 'rigging',
status: 'in_progress',
assigned_user_id: 3
}
]
},
{
id: 2,
name: 'Prop_Sword',
project_id: projectId,
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z',
task_details: [
{
task_id: 203,
task_type: 'modeling',
status: 'not_started',
assigned_user_id: null
}
]
}
];
}
};
const mockTaskService = {
getTasks: async (filters) => {
console.log('Mock: Legacy task service called with filters', filters);
return [
{
id: 301,
name: 'Legacy Task 1',
task_type: 'compositing',
status: 'in_progress',
project_id: 1,
project_name: 'Legacy Project',
created_at: '2024-01-01T00:00:00Z',
updated_at: '2024-01-01T00:00:00Z'
}
];
}
};
// Mock the optimized TasksStore logic
class MockTasksStore {
constructor() {
this.tasks = [];
this.loading = false;
this.error = null;
}
get myTasks() {
return this.tasks;
}
get tasksByStatus() {
return this.tasks.reduce((acc, task) => {
if (!acc[task.status]) {
acc[task.status] = [];
}
acc[task.status].push(task);
return acc;
}, {});
}
get overdueTasks() {
const now = new Date();
return this.tasks.filter(task => {
if (!task.deadline) return false;
const deadline = new Date(task.deadline);
return deadline < now && task.status !== 'approved';
});
}
async fetchTasks(filters = {}) {
this.loading = true;
this.error = null;
try {
if (filters.projectId) {
// Use optimized approach: get both shots and assets with embedded task data
const [shots, assets] = await Promise.all([
mockShotService.getShotsByProject(filters.projectId),
mockAssetService.getAssets(filters.projectId)
]);
// Extract tasks from embedded data
const shotTasks = this.extractTasksFromShots(shots);
const assetTasks = this.extractTasksFromAssets(assets);
// Combine all tasks
let allTasks = [...shotTasks, ...assetTasks];
// Apply additional filters
if (filters.assignedUserId) {
allTasks = allTasks.filter(task => task.assigned_user_id === filters.assignedUserId);
}
if (filters.status) {
allTasks = allTasks.filter(task => task.status === filters.status);
}
if (filters.taskType) {
allTasks = allTasks.filter(task => task.task_type === filters.taskType);
}
this.tasks = allTasks;
} else {
// Fallback to original task service
const response = await mockTaskService.getTasks(filters);
this.tasks = response;
}
} catch (err) {
this.error = err.message || 'Failed to fetch tasks';
console.error('Error fetching tasks:', err);
} finally {
this.loading = false;
}
}
extractTasksFromShots(shots) {
const tasks = [];
for (const shot of shots) {
if (!shot.task_details || shot.task_details.length === 0) {
continue;
}
for (const taskDetail of shot.task_details) {
if (!taskDetail.task_type || !taskDetail.status) {
continue;
}
tasks.push({
id: taskDetail.task_id || 0,
name: `${shot.name} - ${this.formatTaskType(taskDetail.task_type)}`,
task_type: taskDetail.task_type,
status: taskDetail.status,
project_id: shot.project_id,
project_name: shot.project_name || '',
episode_id: shot.episode_id,
shot_id: shot.id,
shot_name: shot.name,
assigned_user_id: taskDetail.assigned_user_id,
created_at: shot.created_at,
updated_at: shot.updated_at
});
}
}
return tasks;
}
extractTasksFromAssets(assets) {
const tasks = [];
for (const asset of assets) {
if (!asset.task_details || asset.task_details.length === 0) {
continue;
}
for (const taskDetail of asset.task_details) {
if (!taskDetail.task_type || !taskDetail.status) {
continue;
}
tasks.push({
id: taskDetail.task_id || 0,
name: `${asset.name} - ${this.formatTaskType(taskDetail.task_type)}`,
task_type: taskDetail.task_type,
status: taskDetail.status,
project_id: asset.project_id,
project_name: '',
asset_id: asset.id,
asset_name: asset.name,
assigned_user_id: taskDetail.assigned_user_id,
created_at: asset.created_at,
updated_at: asset.updated_at
});
}
}
return tasks;
}
formatTaskType(taskType) {
return taskType.charAt(0).toUpperCase() + taskType.slice(1).replace('_', ' ');
}
}
// Create store instance for testing
const tasksStore = new MockTasksStore();
// Test functions
window.testOptimizedFetch = async function() {
addResult('info', 'Testing optimized fetch with projectId...');
try {
await tasksStore.fetchTasks({ projectId: 1 });
const results = {
totalTasks: tasksStore.tasks.length,
shotTasks: tasksStore.tasks.filter(t => t.shot_id).length,
assetTasks: tasksStore.tasks.filter(t => t.asset_id).length,
tasks: tasksStore.tasks
};
addResult('success', `✅ Optimized fetch successful!
Total tasks: ${results.totalTasks}
Shot tasks: ${results.shotTasks}
Asset tasks: ${results.assetTasks}
Tasks combined from both shots and assets:`, JSON.stringify(results.tasks, null, 2));
} catch (error) {
addResult('error', `❌ Optimized fetch failed: ${error.message}`);
}
};
window.testLegacyFetch = async function() {
addResult('info', 'Testing legacy fetch without projectId...');
try {
await tasksStore.fetchTasks({ status: 'in_progress' });
addResult('success', `✅ Legacy fetch successful!
Total tasks: ${tasksStore.tasks.length}
Legacy tasks:`, JSON.stringify(tasksStore.tasks, null, 2));
} catch (error) {
addResult('error', `❌ Legacy fetch failed: ${error.message}`);
}
};
window.testFiltering = async function() {
addResult('info', 'Testing task filtering...');
try {
// First fetch all tasks
await tasksStore.fetchTasks({ projectId: 1 });
const allTasks = tasksStore.tasks.length;
// Test status filtering
await tasksStore.fetchTasks({ projectId: 1, status: 'in_progress' });
const inProgressTasks = tasksStore.tasks.length;
// Test user filtering
await tasksStore.fetchTasks({ projectId: 1, assignedUserId: 1 });
const userTasks = tasksStore.tasks.length;
addResult('success', `✅ Filtering tests successful!
All tasks: ${allTasks}
In progress tasks: ${inProgressTasks}
User 1 tasks: ${userTasks}`);
} catch (error) {
addResult('error', `❌ Filtering test failed: ${error.message}`);
}
};
window.testComputedProperties = async function() {
addResult('info', 'Testing computed properties...');
try {
await tasksStore.fetchTasks({ projectId: 1 });
const myTasks = tasksStore.myTasks;
const tasksByStatus = tasksStore.tasksByStatus;
const overdueTasks = tasksStore.overdueTasks;
addResult('success', `✅ Computed properties working!
My tasks count: ${myTasks.length}
Tasks by status: ${JSON.stringify(Object.keys(tasksByStatus).reduce((acc, status) => {
acc[status] = tasksByStatus[status].length;
return acc;
}, {}), null, 2)}
Overdue tasks: ${overdueTasks.length}`);
} catch (error) {
addResult('error', `❌ Computed properties test failed: ${error.message}`);
}
};
window.clearResults = function() {
document.getElementById('results').innerHTML = '';
};
function addResult(type, title, details = '') {
const resultsDiv = document.getElementById('results');
const resultDiv = document.createElement('div');
resultDiv.className = `test-section ${type}`;
let content = `<h4>${title}</h4>`;
if (details) {
content += `<pre>${details}</pre>`;
}
resultDiv.innerHTML = content;
resultsDiv.appendChild(resultDiv);
}
// Auto-run initial test
setTimeout(() => {
addResult('info', 'TasksStore Optimization Test Ready', 'Click the test buttons above to verify the optimized functionality.');
}, 100);
</script>
</body>
</html>