Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ alwaysApply: false
Default to using Bun instead of Node.js.

- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun test` instead of `jest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
Expand Down
113 changes: 113 additions & 0 deletions .kiro/steering/code-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Coding Guidelines

## General Standards
- **Function Declarations**: Always use arrow functions (`const fn = () => {}`) instead of function declarations (`function fn() {}`)
- **TypeScript**: Use strict typing throughout the codebase
- **Path Aliases**: Always use `@frontend/` and `@backend/` instead of relative paths

## Vue Component Standards

### Single File Component Structure
Vue components MUST follow this exact order:
1. **`<script setup lang="ts">`** - Always at the top
2. **`<template>`** - In the middle
3. **`<style scoped>`** - Should not be used

### Styling Guidelines
- **Tailwind Only**: Always use Tailwind CSS utility classes instead of custom CSS
- **No Custom CSS**: `<style>` sections should not be used
- **Custom CSS Exception**: Only use `<style scoped>` for complex animations or styles that cannot be achieved with Tailwind utilities

### Component Best Practices
- Use `<script setup lang="ts">` syntax exclusively
- Define props with `defineProps<Props>()` and `withDefaults()`
- Define emits with `defineEmits<{}>()`
- Use computed properties for reactive derived state
- Leverage auto-imports for Vue APIs and PrimeVue components

## Auto-Import Configuration

The frontend uses `unplugin-auto-import` and `unplugin-vue-components` for automatic imports.

### Auto-Import Rules
- **Framework APIs**: All Vue, Vue Router, and Pinia APIs are automatically imported
- **Third-party Libraries**: VueUse composables and PrimeVue utilities are auto-imported
- **Custom Code**: All files in `src/**` (all subdirectories under src) are auto-imported
- **Components**: All PrimeVue components and custom components in `src/*` (direct subdirectories) are auto-imported

### Usage Guidelines
- No need to import Vue APIs, VueUse composables, or PrimeVue components
- All exported functions from any file under `src/` are automatically available
- Components from direct subdirectories of `src/` are automatically available
- Type definitions are generated in `auto-imports.d.ts` and `components.d.ts`
- ESLint configuration is automatically updated for auto-imports
- Avoid manual imports for auto-imported items to prevent conflicts

## Path Aliasing

The project uses TypeScript path mapping for clean imports across the monorepo.

### Path Mapping Rules
- `@backend` → `./backend/src`
- `@backend/*` → `./backend/src/*`
- `@backend/tests/*` → `./backend/tests/*`
- `@frontend` → `./frontend/src`
- `@frontend/*` → `./frontend/src/*`

### Best Practices
- Always use path aliases `@frontend/` and `@backend/` instead of relative paths
- Use aliases consistently across both TypeScript and test files
- Path aliases work in both development and build environments

## Code Review Checklist

When reviewing code for compliance, check in this order:

### 1. Basic Structure (Check First)
- [ ] `<script setup lang="ts">` is at the top (Vue components)
- [ ] `<template>` is in the middle (Vue components)
- [ ] `<style scoped>` is at bottom (if present in exception cases)
- [ ] All functions use arrow function syntax

### 2. Styling Compliance
- [ ] No custom CSS that can be replaced with Tailwind utilities
- [ ] All styling uses Tailwind classes where possible
- [ ] `<style>` section only exists for complex animations or unavoidable custom styles

### 3. TypeScript & Composition API
- [ ] Uses `<script setup lang="ts">` syntax (Vue components)
- [ ] Props defined with `defineProps<Props>()` and `withDefaults()` (Vue components)
- [ ] Emits defined with `defineEmits<{}>()` (Vue components)
- [ ] Proper TypeScript typing throughout

### 4. Auto-Imports & Path Aliases
- [ ] No manual imports for auto-imported Vue APIs or PrimeVue components
- [ ] Uses `@frontend/` and `@backend/` path aliases instead of relative paths
- [ ] No import conflicts with auto-import system

## Backend Coding Standards

### API Development
- Use Elysia framework patterns and conventions
- Implement proper error handling with Elysia plugins
- Define schemas in `_schemas.ts` files
- Group routes by domain (e.g., `/api/project/*`)

### Database Operations
- Use DuckDB for data processing operations
- Use SQLite for project persistence
- Implement proper connection management
- Use prepared statements for security

## Testing Standards

### Test Structure
- Backend tests mirror `src/` structure
- Frontend tests in `__tests__/` subdirectories
- Integration tests for API endpoints
- Unit tests for composables and utilities

### Test Naming
- Descriptive test names that explain the behavior being tested
- Group related tests using `describe` blocks
- Use `it` or `test` for individual test cases
43 changes: 0 additions & 43 deletions .kiro/steering/tech.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,49 +27,6 @@
- **Auto-imports**: Automatic imports for Vue composables and PrimeVue components
- **TypeScript**: Strict type checking across the stack

## Coding Standards
- **Function Declarations**: Always use arrow functions (`const fn = () => {}`) instead of function declarations (`function fn() {}`)

## Auto-Import Configuration (Frontend)

The frontend uses `unplugin-auto-import` and `unplugin-vue-components` for automatic imports:

### Auto-Import Rules
- **Framework APIs**: All Vue, Vue Router, and Pinia APIs are automatically imported
- **Third-party Libraries**: VueUse composables and PrimeVue utilities are auto-imported
- **Custom Code**: All files in `src/**` (all subdirectories under src) are auto-imported
- **Components**: All PrimeVue components and custom components in `src/*` (direct subdirectories) are auto-imported

### Usage Guidelines
- No need to import Vue APIs, VueUse composables, or PrimeVue components
- All exported functions from any file under `src/` are automatically available
- Components from direct subdirectories of `src/` are automatically available
- Type definitions are generated in `auto-imports.d.ts` and `components.d.ts`
- ESLint configuration is automatically updated for auto-imports
- Avoid manual imports for auto-imported items to prevent conflicts

## Path Aliasing

The project uses TypeScript path mapping for clean imports across the monorepo.

### Configuration
- **Base Config**: `tsconfig.base.json` defines all path mappings
- **Frontend**: Uses `vite-tsconfig-paths` plugin for Vite compatibility
- **Backend**: Inherits paths from base configuration
- **Root Level**: Paths resolve from project root directory

### Path Mapping Rules
- `@backend` → `./backend/src`
- `@backend/*` → `./backend/src/*`
- `@backend/tests/*` → `./backend/tests/*`
- `@frontend` → `./frontend/src`
- `@frontend/*` → `./frontend/src/*`

### Best Practices
- Always use path aliases `@frontend/` and `@backend/` but not relative paths
- Use aliases consistently across both TypeScript and test files
- Path aliases work in both development and build environments

## Common Commands

### Development
Expand Down
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@types/node": "catalog:",
"@typescript-eslint/eslint-plugin": "catalog:",
"@typescript-eslint/parser": "catalog:",
"bun-types": "catalog:",
"eslint": "catalog:",
"eslint-config-prettier": "catalog:",
"eslint-plugin-prettier": "catalog:",
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "vite build",
"preview": "vite preview",
"lint": "eslint .",
"typecheck": "vue-tsc --build"
"typecheck": "vue-tsc"
},
"dependencies": {
"@elysiajs/eden": "catalog:",
Expand Down Expand Up @@ -42,6 +42,7 @@
"@vue/eslint-config-typescript": "catalog:",
"@vue/tsconfig": "^0.7.0",
"autoprefixer": "^10.4.21",
"bun-types": "catalog:",
"elysia": "catalog:",
"eslint": "catalog:",
"eslint-plugin-prettier": "catalog:",
Expand Down
55 changes: 33 additions & 22 deletions frontend/src/composables/__tests__/useDragDropContext.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect, beforeEach } from 'bun:test'
import { useDragDropContext, createDropZoneConfig } from '@frontend/composables/useDragDropContext'
import type { ColumnInfo, WikibaseDataType } from '@frontend/types/schema-mapping'
import type { ColumnInfo, WikibaseDataType } from '@frontend/types/wikibase-schema'
import type { DropTarget } from '@frontend/types/drag-drop'

describe('useDragDropContext', () => {
Expand Down Expand Up @@ -95,11 +95,12 @@ describe('useDragDropContext', () => {
describe('Drop Validation', () => {
it('should validate compatible data types', () => {
const context = useDragDropContext()

const validation = context.validateDrop(mockColumnInfo, mockDropTarget)

expect(validation.isValid).toBe(true)
expect(validation.reason).toBe('Compatible mapping')
expect(validation).toEqual({
isValid: true,
reason: 'Compatible mapping',
})
})

it('should reject incompatible data types', () => {
Expand All @@ -113,9 +114,10 @@ describe('useDragDropContext', () => {

const validation = context.validateDrop(numericColumn, mockDropTarget)

expect(validation.isValid).toBe(false)
expect(validation.reason).toContain('not compatible')
expect(validation.suggestions).toBeDefined()
expect(validation).toEqual({
isValid: false,
reason: "Column type 'INTEGER' is not compatible with target types: string",
})
})

it('should reject nullable columns for required targets', () => {
Expand All @@ -131,9 +133,10 @@ describe('useDragDropContext', () => {

const validation = context.validateDrop(nullableColumn, requiredTarget)

expect(validation.isValid).toBe(false)
expect(validation.reason).toContain('Required field cannot accept nullable column')
expect(validation.suggestions).toContain('Use a non-nullable column')
expect(validation).toEqual({
isValid: false,
reason: 'Required field cannot accept nullable column',
})
})

it('should validate statement targets with property IDs', () => {
Expand All @@ -147,7 +150,10 @@ describe('useDragDropContext', () => {

const validation = context.validateDrop(mockColumnInfo, statementTarget)

expect(validation.isValid).toBe(true)
expect(validation).toEqual({
isValid: true,
reason: 'Compatible mapping',
})
})

it('should reject statement targets without property IDs', () => {
Expand All @@ -161,8 +167,10 @@ describe('useDragDropContext', () => {

const validation = context.validateDrop(mockColumnInfo, statementTarget)

expect(validation.isValid).toBe(false)
expect(validation.reason).toContain('must have a property ID')
expect(validation).toEqual({
isValid: false,
reason: 'Statement target must have a property ID',
})
})

it('should validate label length constraints', () => {
Expand All @@ -176,9 +184,10 @@ describe('useDragDropContext', () => {

const validation = context.validateDrop(longValueColumn, mockDropTarget)

expect(validation.isValid).toBe(false)
expect(validation.reason).toContain('should be shorter than')
expect(validation.suggestions).toContain('Use description field instead')
expect(validation).toEqual({
isValid: false,
reason: 'label values should be shorter than 250 characters',
})
})
})

Expand Down Expand Up @@ -295,12 +304,14 @@ describe('createDropZoneConfig', () => {
const onDropCallback = () => {}
const config = createDropZoneConfig('item.terms.labels.en', ['string'], onDropCallback)

expect(config.acceptedDataTypes).toEqual(['application/x-column-data'])
expect(config.onDrop).toBeDefined()
expect(config.onDragOver).toBeDefined()
expect(config.onDragEnter).toBeDefined()
expect(config.onDragLeave).toBeDefined()
expect(config.validateDrop).toBeDefined()
expect(config).toEqual({
acceptedDataTypes: ['application/x-column-data'],
onDrop: expect.any(Function),
onDragOver: expect.any(Function),
onDragEnter: expect.any(Function),
onDragLeave: expect.any(Function),
validateDrop: expect.any(Function),
})
})

it('should validate drop data correctly', () => {
Expand Down
Loading