450 lines
17 KiB
HTML
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> |