316 lines
10 KiB
HTML
316 lines
10 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Task Status Test</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.test-section {
|
|
margin: 20px 0;
|
|
padding: 20px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.success {
|
|
color: green;
|
|
}
|
|
|
|
.error {
|
|
color: red;
|
|
}
|
|
|
|
.info {
|
|
color: blue;
|
|
}
|
|
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 10px 0;
|
|
}
|
|
|
|
th,
|
|
td {
|
|
border: 1px solid #ddd;
|
|
padding: 8px;
|
|
text-align: left;
|
|
}
|
|
|
|
th {
|
|
background-color: #f2f2f2;
|
|
}
|
|
|
|
.status-badge {
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.status-not-started {
|
|
background-color: #f3f4f6;
|
|
color: #374151;
|
|
}
|
|
|
|
.status-in-progress {
|
|
background-color: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.status-submitted {
|
|
background-color: #fef3c7;
|
|
color: #92400e;
|
|
}
|
|
|
|
.status-approved {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
}
|
|
|
|
.status-retake {
|
|
background-color: #fee2e2;
|
|
color: #991b1b;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<h1>VFX Project Management - Task Status Test</h1>
|
|
|
|
<div class="test-section">
|
|
<h2>API Connection Test</h2>
|
|
<div id="connection-status">Testing connection...</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Asset List with Task Status</h2>
|
|
<div id="asset-status">Loading assets...</div>
|
|
<div id="asset-table"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Task Status Filtering Test</h2>
|
|
<div>
|
|
<label>Filter by task status:</label>
|
|
<select id="status-filter">
|
|
<option value="">All Tasks</option>
|
|
<option value="modeling:not_started">Modeling - Not Started</option>
|
|
<option value="modeling:in_progress">Modeling - In Progress</option>
|
|
<option value="surfacing:not_started">Surfacing - Not Started</option>
|
|
<option value="rigging:not_started">Rigging - Not Started</option>
|
|
</select>
|
|
<button onclick="applyFilter()">Apply Filter</button>
|
|
</div>
|
|
<div id="filtered-results"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Task Status Sorting Test</h2>
|
|
<div>
|
|
<label>Sort by:</label>
|
|
<select id="sort-field">
|
|
<option value="name">Name</option>
|
|
<option value="category">Category</option>
|
|
<option value="modeling_status">Modeling Status</option>
|
|
<option value="surfacing_status">Surfacing Status</option>
|
|
<option value="rigging_status">Rigging Status</option>
|
|
</select>
|
|
<select id="sort-direction">
|
|
<option value="asc">Ascending</option>
|
|
<option value="desc">Descending</option>
|
|
</select>
|
|
<button onclick="applySorting()">Apply Sorting</button>
|
|
</div>
|
|
<div id="sorted-results"></div>
|
|
</div>
|
|
|
|
<script>
|
|
const BASE_URL = 'http://localhost:8000';
|
|
let authToken = '';
|
|
let currentProjectId = null;
|
|
let allAssets = [];
|
|
|
|
// Test API connection and login
|
|
async function testConnection() {
|
|
try {
|
|
// Login
|
|
const loginResponse = await fetch(`${BASE_URL}/auth/login`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
email: 'admin@vfx.com',
|
|
password: 'admin123'
|
|
})
|
|
});
|
|
|
|
if (!loginResponse.ok) {
|
|
throw new Error(`Login failed: ${loginResponse.status}`);
|
|
}
|
|
|
|
const loginData = await loginResponse.json();
|
|
authToken = loginData.access_token;
|
|
|
|
// Get projects
|
|
const projectsResponse = await fetch(`${BASE_URL}/projects/`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${authToken}`
|
|
}
|
|
});
|
|
|
|
if (!projectsResponse.ok) {
|
|
throw new Error(`Projects fetch failed: ${projectsResponse.status}`);
|
|
}
|
|
|
|
const projects = await projectsResponse.json();
|
|
if (projects.length === 0) {
|
|
throw new Error('No projects found');
|
|
}
|
|
|
|
currentProjectId = projects[0].id;
|
|
|
|
document.getElementById('connection-status').innerHTML =
|
|
`<span class="success">✅ Connected successfully! Using project: ${projects[0].name} (ID: ${currentProjectId})</span>`;
|
|
|
|
// Load assets
|
|
await loadAssets();
|
|
|
|
} catch (error) {
|
|
document.getElementById('connection-status').innerHTML =
|
|
`<span class="error">❌ Connection failed: ${error.message}</span>`;
|
|
}
|
|
}
|
|
|
|
// Load assets with task status
|
|
async function loadAssets(options = {}) {
|
|
try {
|
|
const params = new URLSearchParams({
|
|
project_id: currentProjectId.toString(),
|
|
...options
|
|
});
|
|
|
|
const response = await fetch(`${BASE_URL}/assets/?${params}`, {
|
|
headers: {
|
|
'Authorization': `Bearer ${authToken}`
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Assets fetch failed: ${response.status}`);
|
|
}
|
|
|
|
const assets = await response.json();
|
|
allAssets = assets;
|
|
|
|
document.getElementById('asset-status').innerHTML =
|
|
`<span class="success">✅ Loaded ${assets.length} assets with task status</span>`;
|
|
|
|
displayAssetsTable(assets);
|
|
|
|
} catch (error) {
|
|
document.getElementById('asset-status').innerHTML =
|
|
`<span class="error">❌ Failed to load assets: ${error.message}</span>`;
|
|
}
|
|
}
|
|
|
|
// Display assets in a table
|
|
function displayAssetsTable(assets) {
|
|
if (assets.length === 0) {
|
|
document.getElementById('asset-table').innerHTML = '<p>No assets found</p>';
|
|
return;
|
|
}
|
|
|
|
let tableHTML = `
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Category</th>
|
|
<th>Task Count</th>
|
|
<th>Modeling</th>
|
|
<th>Surfacing</th>
|
|
<th>Rigging</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
`;
|
|
|
|
assets.forEach(asset => {
|
|
const modelingStatus = asset.task_status?.modeling || 'not_started';
|
|
const surfacingStatus = asset.task_status?.surfacing || 'not_started';
|
|
const riggingStatus = asset.task_status?.rigging || 'not_started';
|
|
|
|
tableHTML += `
|
|
<tr>
|
|
<td>${asset.name}</td>
|
|
<td>${asset.category}</td>
|
|
<td>${asset.task_count}</td>
|
|
<td><span class="status-badge status-${modelingStatus.replace('_', '-')}">${formatStatus(modelingStatus)}</span></td>
|
|
<td><span class="status-badge status-${surfacingStatus.replace('_', '-')}">${formatStatus(surfacingStatus)}</span></td>
|
|
<td>${isRiggingApplicable(asset.category) ?
|
|
`<span class="status-badge status-${riggingStatus.replace('_', '-')}">${formatStatus(riggingStatus)}</span>` :
|
|
'<span class="info">N/A</span>'}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
tableHTML += '</tbody></table>';
|
|
document.getElementById('asset-table').innerHTML = tableHTML;
|
|
}
|
|
|
|
// Apply task status filter
|
|
async function applyFilter() {
|
|
const filter = document.getElementById('status-filter').value;
|
|
const options = {};
|
|
|
|
if (filter) {
|
|
options.task_status_filter = filter;
|
|
}
|
|
|
|
await loadAssets(options);
|
|
document.getElementById('filtered-results').innerHTML =
|
|
`<span class="info">Applied filter: ${filter || 'None'}</span>`;
|
|
}
|
|
|
|
// Apply sorting
|
|
async function applySorting() {
|
|
const sortField = document.getElementById('sort-field').value;
|
|
const sortDirection = document.getElementById('sort-direction').value;
|
|
|
|
const options = {
|
|
sort_by: sortField,
|
|
sort_direction: sortDirection
|
|
};
|
|
|
|
await loadAssets(options);
|
|
document.getElementById('sorted-results').innerHTML =
|
|
`<span class="info">Sorted by: ${sortField} (${sortDirection})</span>`;
|
|
}
|
|
|
|
// Helper functions
|
|
function formatStatus(status) {
|
|
return status.split('_').map(word =>
|
|
word.charAt(0).toUpperCase() + word.slice(1)
|
|
).join(' ');
|
|
}
|
|
|
|
function isRiggingApplicable(category) {
|
|
return category === 'characters' || category === 'vehicles';
|
|
}
|
|
|
|
// Initialize
|
|
testConnection();
|
|
</script>
|
|
</body>
|
|
|
|
</html>"" |