import '@material/web/button/filled-button.js' import '@material/web/button/outlined-button.js' import '@material/web/icon/icon.js' import '@material/web/iconbutton/icon-button.js' import { css, html } from 'lit' import { customElement, state } from 'lit/decorators.js' import { client, PageView, navigate } from '@things-factory/shell/client' import gql from 'graphql-tag' @customElement('label-studio-project-list') export class LabelStudioProjectList extends PageView { static styles = [ css` :host { display: flex; flex-direction: column; height: 100%; padding: 20px; background-color: var(--md-sys-color-surface); } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .header h2 { margin: 0; color: var(--md-sys-color-on-surface); } .project-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; overflow-y: auto; } .project-card { background: var(--md-sys-color-surface-container); border-radius: 12px; padding: 20px; cursor: pointer; transition: all 0.3s ease; } .project-card:hover { background: var(--md-sys-color-surface-container-high); transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } .project-card h3 { margin: 0 0 10px 0; color: var(--md-sys-color-on-surface); } .project-card p { margin: 5px 0; color: var(--md-sys-color-on-surface-variant); font-size: 14px; } .project-stats { display: flex; gap: 15px; margin-top: 15px; } .stat-item { display: flex; flex-direction: column; } .stat-label { font-size: 12px; color: var(--md-sys-color-on-surface-variant); } .stat-value { font-size: 20px; font-weight: bold; color: var(--md-sys-color-primary); } .progress-bar { width: 100%; height: 8px; background: var(--md-sys-color-surface-variant); border-radius: 4px; margin-top: 10px; overflow: hidden; } .progress-fill { height: 100%; background: var(--md-sys-color-primary); transition: width 0.3s ease; } .loading { display: flex; justify-content: center; align-items: center; height: 100%; font-size: 18px; color: var(--md-sys-color-on-surface-variant); } .error { color: var(--md-sys-color-error); padding: 20px; text-align: center; } ` ] @state() projects: any[] = [] @state() loading: boolean = true @state() error: string = '' async pageUpdated(changes, lifecycle, changedBefore) { if (this.active) { this.loadProjects() } } async loadProjects() { try { this.loading = true const response = await client.query({ query: gql` query { labelStudioProjects { id title description taskCount completedTaskCount completionRate createdAt updatedAt } } ` }) this.projects = response.data.labelStudioProjects this.loading = false } catch (error: any) { console.error('Failed to load projects:', error) this.error = error.message || 'Failed to load projects' this.loading = false } } createProject() { navigate('label-studio-project-create') } openProject(projectId: number) { // Open Label Studio viewer with project navigate(`label-studio-label/${projectId}`) } render() { if (this.loading) { return html`
Loading projects...
` } if (this.error) { return html`
${this.error}
` } return html`

Label Studio Projects

add New Project
${this.projects.map( project => html`
this.openProject(project.id)}>

${project.title}

${project.description || 'No description'}

Tasks ${project.taskCount}
Completed ${project.completedTaskCount}
` )}
` } }