From 5674c94211bf822c800601dd9e31e38d05d04eb7 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 18 Aug 2026 15:31:10 -0700 Subject: [PATCH 1/2] Refactor project categorization to use project types and labels across the application --- frontend/src/api/client.ts | 11 +- frontend/src/components/BotCard.vue | 217 +++++++++++----- frontend/src/pages/BotDetailPage.vue | 48 ++-- frontend/src/pages/HomePage.vue | 358 ++++++++++++++++++--------- frontend/src/pages/SubmitPage.vue | 146 ++++++++--- frontend/src/types/project.ts | 15 +- 6 files changed, 563 insertions(+), 232 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 2c760a6..aa7bcd6 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1,8 +1,9 @@ import type { - Category, + Label, Project, ProjectCreate, ProjectListItem, + ProjectType, } from '../types/project' interface Profile { @@ -110,8 +111,12 @@ export function deleteProject(id: string) { }) } -export function listCategories() { - return request('/categories/') +export function listProjectTypes() { + return request('/project-types/') +} + +export function listLabels() { + return request('/labels/') } export function createProject(input: ProjectCreate) { diff --git a/frontend/src/components/BotCard.vue b/frontend/src/components/BotCard.vue index a6c1436..df91e03 100644 --- a/frontend/src/components/BotCard.vue +++ b/frontend/src/components/BotCard.vue @@ -1,85 +1,116 @@ + \ No newline at end of file diff --git a/frontend/src/pages/BotDetailPage.vue b/frontend/src/pages/BotDetailPage.vue index 4cb4f68..673a1ce 100644 --- a/frontend/src/pages/BotDetailPage.vue +++ b/frontend/src/pages/BotDetailPage.vue @@ -27,7 +27,7 @@ async function loadProject() { error.value = err instanceof Error ? err.message - : 'Could not load this bot.' + : 'Could not load this project.' } finally { loading.value = false } @@ -46,7 +46,7 @@ onMounted(loadProject) v-if="loading" class="py-20 font-mono text-xs text-[var(--faint)]" > - Loading bot… + Loading project… @@ -107,14 +107,19 @@ onMounted(loadProject) {{ project.short_description }}

- +
+ {{ project.project_type.name }} + + - {{ category.name }} + {{ label.name }}
@@ -150,7 +155,7 @@ onMounted(loadProject) class="mt-12 grid gap-10 lg:grid-cols-[minmax(0,1fr)_300px]" > -
+

@@ -244,24 +249,37 @@ onMounted(loadProject)
- +
- Categories + Project type - {{ - project.categories - .map((category) => category.name) - .join(', ') - }} + {{ project.project_type.name }} + +
+ + +
+ + Labels + + + + {{ project.labels.map((label) => label.name).join(', ') || 'None' }}
diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue index bfbb678..43b9468 100644 --- a/frontend/src/pages/HomePage.vue +++ b/frontend/src/pages/HomePage.vue @@ -12,17 +12,22 @@ import type { ProjectListItem } from '../types/project' const projects = ref([]) const query = ref('') -const category = ref('') +const projectTypeFilter = ref('') +const labelFilter = ref('') const loading = ref(true) const error = ref('') -const botCount = computed(() => projects.value.length) +const projectCount = computed(() => projects.value.length) -const categories = computed(() => +const projectTypes = computed(() => + [...new Set(projects.value.map((project) => project.project_type.name))].sort(), +) + +const labels = computed(() => [ ...new Set( projects.value.flatMap((project) => - project.categories.map((item) => item.name), + project.labels.map((item) => item.name), ), ), ].sort(), @@ -55,32 +60,44 @@ const visibleProjects = computed(() => { project.name, project.short_description, project.description, + project.project_type.name, + ...project.labels.map((item) => item.name), ] .join(' ') .toLowerCase() .includes(normalizedQuery) - const matchesCategory = - !category.value || - project.categories.some( - (item) => item.name === category.value, - ) + const matchesProjectType = + !projectTypeFilter.value || + project.project_type.name === projectTypeFilter.value - return matchesQuery && matchesCategory + const matchesLabel = + !labelFilter.value || + project.labels.some((item) => item.name === labelFilter.value) + + return matchesQuery && matchesProjectType && matchesLabel }) }) const hasFilters = computed( - () => Boolean(query.value.trim()) || Boolean(category.value), + () => + Boolean(query.value.trim()) || + Boolean(projectTypeFilter.value) || + Boolean(labelFilter.value), ) -function selectCategory(value: string) { - category.value = category.value === value ? '' : value +function selectProjectType(value: string) { + projectTypeFilter.value = projectTypeFilter.value === value ? '' : value +} + +function selectLabel(value: string) { + labelFilter.value = labelFilter.value === value ? '' : value } function clearFilters() { query.value = '' - category.value = '' + projectTypeFilter.value = '' + labelFilter.value = '' } async function loadProjects() { @@ -93,7 +110,7 @@ async function loadProjects() { error.value = err instanceof Error ? err.message - : 'Could not load bots.' + : 'Could not load projects.' } finally { loading.value = false } @@ -103,96 +120,228 @@ onMounted(loadProjects)