From 282a0295ecd2b95d07cf72284f85ea89d4d7c753 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Mon, 1 Dec 2025 19:16:37 +0000 Subject: [PATCH] feat: implement item detail page and form components - Created ItemForm component with fields for all metadata (priority, due date, tags, notes) - Created ItemDetail component to display all item information - Built /list/[id]/item/[itemId] page for viewing full item details - Built /list/[id]/item/[itemId]/edit page for editing items - Added form validation for item inputs (title required, length limits) - Created DeleteItemButton and ToggleItemButton helper components - Enhanced createItem action to support full metadata on creation - Added getItem action to fetch individual items with list data Requirements: 1.1, 1.2, 5.1, 5.2, 5.3, 5.4 --- .kiro/specs/ultimate-lists-app/design.md | 536 ++++++++++++++++++ .../specs/ultimate-lists-app/requirements.md | 114 ++++ .kiro/specs/ultimate-lists-app/tasks.md | 6 +- .vscode/settings.json | 2 + app/list/[id]/item/[itemId]/edit/page.tsx | 41 ++ app/list/[id]/item/[itemId]/page.tsx | 54 ++ components/DeleteItemButton.tsx | 60 ++ components/ItemDetail.tsx | 163 ++++++ components/ItemForm.tsx | 267 +++++++++ components/ToggleItemButton.tsx | 45 ++ lib/actions.ts | 25 +- 11 files changed, 1309 insertions(+), 4 deletions(-) create mode 100644 .kiro/specs/ultimate-lists-app/design.md create mode 100644 .kiro/specs/ultimate-lists-app/requirements.md create mode 100644 .vscode/settings.json create mode 100644 app/list/[id]/item/[itemId]/edit/page.tsx create mode 100644 app/list/[id]/item/[itemId]/page.tsx create mode 100644 components/DeleteItemButton.tsx create mode 100644 components/ItemDetail.tsx create mode 100644 components/ItemForm.tsx create mode 100644 components/ToggleItemButton.tsx diff --git a/.kiro/specs/ultimate-lists-app/design.md b/.kiro/specs/ultimate-lists-app/design.md new file mode 100644 index 0000000..c978810 --- /dev/null +++ b/.kiro/specs/ultimate-lists-app/design.md @@ -0,0 +1,536 @@ +# Design Document: Ultimate Lists App + +## Overview + +The Ultimate Lists App is a Next.js 14-based Progressive Web App that provides an enhanced task management experience. The application uses a server-side architecture with React Server Components for optimal performance, Prisma ORM with MySQL for data persistence, and PWA capabilities for offline functionality and home screen installation on iOS devices. + +The system is built on a three-tier architecture: +1. **Presentation Layer**: React components with Server Components and Client Components +2. **Business Logic Layer**: Server Actions for data mutations and queries +3. **Data Layer**: Prisma ORM with MySQL database + +The application leverages Next.js App Router for routing, Server Actions for data mutations, and implements PWA features through a service worker and web app manifest. + +## Architecture + +### High-Level Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ Client Browser │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ React Components (RSC + Client) │ │ +│ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │ +│ │ │ Pages │ │ Components │ │ Hooks │ │ │ +│ │ └────────────┘ └────────────┘ └───────────┘ │ │ +│ └──────────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Service Worker (PWA) │ │ +│ │ Cache API + Offline Support │ │ +│ └──────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ + │ HTTP/Server Actions + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Next.js Server │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Server Actions │ │ +│ │ (lib/actions.ts - Business Logic) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Prisma Client │ │ +│ │ (lib/db.ts - Data Access) │ │ +│ └──────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ + │ SQL + ▼ +┌─────────────────────────────────────────────────────────┐ +│ MySQL Database │ +│ (Lists, Items, Metadata) │ +└─────────────────────────────────────────────────────────┘ +``` + +### Technology Stack + +- **Framework**: Next.js 14 with App Router +- **Language**: TypeScript +- **UI Library**: React 18 with Server Components +- **Styling**: Tailwind CSS +- **Database**: MySQL +- **ORM**: Prisma +- **Icons**: Lucide React +- **PWA**: Service Worker + Web App Manifest + +## Components and Interfaces + +### Data Access Layer + +**Prisma Client (`lib/db.ts`)** +- Singleton Prisma client instance +- Connection pooling management +- Type-safe database queries + +**Server Actions (`lib/actions.ts`)** +- `getLists()`: Retrieve all lists with item counts +- `createList(title, color)`: Create a new list +- `updateList(id, data)`: Update list properties +- `deleteList(id)`: Delete a list and cascade to items +- `getListWithItems(listId)`: Retrieve a specific list with all items +- `createItem(listId, title, description, metadata)`: Create a new item +- `updateItem(id, data)`: Update item properties +- `deleteItem(id)`: Delete an item +- `toggleItem(id)`: Toggle item completion status +- `searchItems(query)`: Search across all items +- `getItemsByFilter(listId, filters)`: Filter items by criteria + +### Presentation Layer + +**Pages** +- `/` - Home page displaying all lists +- `/list/[id]` - Individual list view with items +- `/list/[id]/item/[itemId]` - Item detail view + +**Components** +- `ListCard`: Display list summary with item count +- `ListForm`: Create/edit list form +- `ItemCard`: Display individual item +- `ItemForm`: Create/edit item form with metadata +- `ItemDetail`: Full item view with all metadata +- `SearchBar`: Search input with live results +- `FilterPanel`: Filter controls for sorting and filtering +- `PriorityBadge`: Visual priority indicator +- `DueDateDisplay`: Due date with overdue indication +- `TagList`: Display and manage item tags + +**Client Hooks** +- `useOptimisticUpdate`: Optimistic UI updates +- `useSearch`: Search functionality with debouncing +- `useFilters`: Filter state management +- `useOfflineSync`: Handle offline data synchronization + +### PWA Layer + +**Service Worker (`public/sw.js`)** +- Cache static assets (HTML, CSS, JS, images) +- Cache API responses with stale-while-revalidate strategy +- Offline fallback pages +- Background sync for pending mutations + +**Web App Manifest (`public/manifest.json`)** +- App metadata (name, icons, colors) +- Display mode: standalone +- Start URL and scope +- Icon definitions for various sizes + +## Data Models + +### Database Schema + +**List Model** +```typescript +{ + id: string (cuid) + title: string + color: string (hex color) + createdAt: DateTime + updatedAt: DateTime + items: Item[] +} +``` + +**Item Model** +```typescript +{ + id: string (cuid) + title: string + description: string | null + completed: boolean + priority: 'low' | 'medium' | 'high' + dueDate: DateTime | null + tags: string[] (JSON) + notes: string | null + createdAt: DateTime + updatedAt: DateTime + listId: string + list: List +} +``` + +### Client State Models + +**FilterState** +```typescript +{ + priority: 'low' | 'medium' | 'high' | null + completed: boolean | null + tags: string[] + dueDateRange: { start: Date | null, end: Date | null } + sortBy: 'createdAt' | 'dueDate' | 'priority' | 'title' + sortOrder: 'asc' | 'desc' +} +``` + +**SearchState** +```typescript +{ + query: string + results: Item[] + isSearching: boolean +} +``` + +## Correctness Properties + +*A property is a characteristic or behavior that should hold true across all valid executions of a system—essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* + + +### Task Management Properties + +**Property 1: Task creation preserves description** +*For any* valid task description and list, creating a task with that description should result in a task that, when retrieved, has the exact same description. +**Validates: Requirements 1.1** + +**Property 2: Task updates preserve identity** +*For any* task and any valid update data, updating the task should preserve the task's unique identifier while changing the specified fields. +**Validates: Requirements 1.2** + +**Property 3: Task deletion removes from queries** +*For any* task, after deleting that task, querying for tasks should not return the deleted task. +**Validates: Requirements 1.3** + +**Property 4: Task metadata round-trip** +*For any* task and any valid metadata (priority, due date, tags, notes), setting the metadata then retrieving the task should return equivalent metadata values. +**Validates: Requirements 1.4, 5.1, 5.2, 5.3, 5.4** + +**Property 5: Task completion toggle** +*For any* task, toggling its completion status should result in the opposite completion state when retrieved. +**Validates: Requirements 1.5** + +### List Management Properties + +**Property 6: List creation with unique identifiers** +*For any* valid list name, creating a list should result in a list with a unique identifier that differs from all existing list identifiers. +**Validates: Requirements 2.1** + +**Property 7: List retrieval completeness** +*For any* set of created lists, querying all lists should return exactly those lists with accurate item counts. +**Validates: Requirements 2.2** + +**Property 8: List filtering correctness** +*For any* list, querying items for that list should return only items where the listId matches, and should return all such items. +**Validates: Requirements 2.3** + +**Property 9: Cascade deletion** +*For any* list with associated items, deleting the list should result in both the list and all its items being removed from the database. +**Validates: Requirements 2.4** + +**Property 10: List rename preserves items** +*For any* list with items, renaming the list should preserve the count and identifiers of all associated items. +**Validates: Requirements 2.5** + +### Data Persistence Properties + +**Property 11: Application state round-trip** +*For any* application state (lists and items), persisting the state, simulating an app restart, and loading the state should result in equivalent data. +**Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5** + +### Offline and PWA Properties + +**Property 12: Offline resource availability** +*For any* cached resource, when the application is offline, requesting that resource should succeed using the cached version. +**Validates: Requirements 4.4** + +**Property 13: Offline mutation synchronization** +*For any* mutation performed while offline, when connectivity is restored, the mutation should be applied to the server and reflected in subsequent queries. +**Validates: Requirements 4.5** + +### Enhanced Features Properties + +**Property 14: Overdue task identification** +*For any* task with a due date in the past relative to the current time, the task should be marked as overdue. +**Validates: Requirements 5.5** + +### Sorting and Filtering Properties + +**Property 15: Sort order correctness** +*For any* list of tasks and sort criteria (createdAt, dueDate, priority, title), the sorted result should be ordered according to the specified criteria and direction. +**Validates: Requirements 6.1** + +**Property 16: Filter matching correctness** +*For any* filter criteria, all returned tasks should match the criteria, and all tasks in the database matching the criteria should be returned. +**Validates: Requirements 6.2** + +**Property 17: Compound filter conjunction** +*For any* set of multiple filter criteria, all returned tasks should match every criterion (AND logic). +**Validates: Requirements 6.3** + +**Property 18: Filter clear restoration** +*For any* filtered view, clearing all filters should return the complete unfiltered task list. +**Validates: Requirements 6.4** + +**Property 19: Null due date sorting** +*For any* list of tasks sorted by due date, tasks without due dates should appear after all tasks with due dates. +**Validates: Requirements 6.5** + +### Navigation Properties + +**Property 20: Scroll position preservation** +*For any* scroll position in a view, navigating away and returning to that view should restore the same scroll position. +**Validates: Requirements 7.4** + +### Search Properties + +**Property 21: Search result completeness** +*For any* search query, all returned tasks should contain the query text in their description, notes, or tags, and all tasks containing the query should be in the results. +**Validates: Requirements 8.1** + +**Property 22: Search highlight correctness** +*For any* search query and matching text, the highlight function should mark all occurrences of the query text in the result. +**Validates: Requirements 8.2** + +**Property 23: Search clear restoration** +*For any* search state, clearing the search query should restore the previous unfiltered view. +**Validates: Requirements 8.3** + +**Property 24: Search scope correctness** +*For any* search query, when no list is selected, results should include matches from all lists; when a list is selected, results should only include matches from that list. +**Validates: Requirements 8.5** + +## Error Handling + +### Database Errors + +**Connection Failures** +- Retry logic with exponential backoff +- Graceful degradation to cached data +- User notification of connectivity issues + +**Constraint Violations** +- Validate data before database operations +- Return descriptive error messages +- Prevent orphaned records through transactions + +**Query Failures** +- Catch and log database errors +- Return empty results rather than crashing +- Provide user-friendly error messages + +### Client-Side Errors + +**Invalid Input** +- Validate form inputs before submission +- Display inline validation errors +- Prevent submission of invalid data + +**Network Errors** +- Queue mutations for retry when offline +- Show offline indicator +- Sync when connection restored + +**State Inconsistencies** +- Implement optimistic updates with rollback +- Validate state before rendering +- Recover from corrupted local storage + +### PWA-Specific Errors + +**Service Worker Registration** +- Gracefully handle registration failures +- Fall back to online-only mode +- Log errors for debugging + +**Cache Failures** +- Handle quota exceeded errors +- Implement cache eviction strategy +- Provide fallback for uncached resources + +## Testing Strategy + +### Unit Testing + +The application will use **Vitest** as the testing framework for unit tests. Unit tests will focus on: + +**Data Access Layer** +- Server action functions (create, read, update, delete operations) +- Database query correctness +- Error handling in database operations + +**Business Logic** +- Filter logic implementation +- Sort algorithm correctness +- Search algorithm accuracy +- Date calculations (overdue detection) + +**Utility Functions** +- Data serialization/deserialization +- Input validation +- Text highlighting for search results + +**Component Logic** +- Form validation +- State management +- Event handlers + +Unit tests will use realistic test data and verify specific examples, edge cases, and error conditions. They complement property-based tests by catching concrete bugs in specific scenarios. + +### Property-Based Testing + +The application will use **fast-check** as the property-based testing library. Property-based tests will verify the universal correctness properties defined in this document. + +**Configuration Requirements:** +- Each property-based test MUST run a minimum of 100 iterations +- Each test MUST be tagged with a comment referencing the correctness property +- Tag format: `// Feature: ultimate-lists-app, Property {number}: {property_text}` +- Each correctness property MUST be implemented by a SINGLE property-based test + +**Property Test Coverage:** +- Task CRUD operations (Properties 1-5) +- List CRUD operations (Properties 6-10) +- Data persistence and round-trips (Property 11) +- Offline functionality (Properties 12-13) +- Enhanced features (Property 14) +- Sorting and filtering (Properties 15-19) +- Navigation behavior (Property 20) +- Search functionality (Properties 21-24) + +**Test Data Generation:** +Property-based tests will use custom generators for: +- Valid task descriptions (non-empty strings) +- List names (non-empty strings) +- Priority levels (low, medium, high) +- Due dates (past, present, future) +- Tags (arrays of strings) +- Filter criteria (combinations of filters) +- Sort options (field and direction) +- Search queries (substrings of existing data) + +### Integration Testing + +Integration tests will verify: +- End-to-end user flows (create list → add items → complete items) +- Server Action integration with database +- PWA installation and offline functionality +- Service worker caching behavior + +### Manual Testing + +Manual testing will cover: +- PWA installation on iOS Safari +- Offline functionality in real-world scenarios +- UI/UX on various screen sizes +- Touch interaction quality +- Visual design and accessibility + +## Performance Considerations + +### Database Optimization + +- Index frequently queried fields (listId, completed, dueDate, priority) +- Use database-level sorting and filtering where possible +- Implement pagination for large lists +- Use connection pooling for efficient database access + +### Client-Side Optimization + +- Use React Server Components for initial page loads +- Implement optimistic updates for perceived performance +- Lazy load item details +- Debounce search input +- Virtualize long lists + +### Caching Strategy + +- Cache static assets aggressively (CSS, JS, images) +- Use stale-while-revalidate for API responses +- Implement cache versioning for updates +- Set appropriate cache expiration times + +### Bundle Optimization + +- Code splitting by route +- Tree shaking unused code +- Minimize and compress assets +- Use Next.js automatic optimizations + +## Security Considerations + +### Input Validation + +- Sanitize all user inputs +- Validate data types and formats +- Prevent SQL injection through Prisma's parameterized queries +- Limit input lengths to prevent abuse + +### Data Protection + +- Use environment variables for sensitive configuration +- Implement HTTPS in production +- Secure database credentials +- Validate all server-side inputs + +### PWA Security + +- Serve service worker over HTTPS +- Validate cached resources +- Implement Content Security Policy +- Use Subresource Integrity for external resources + +## Deployment Considerations + +### Environment Setup + +- Development: Local MySQL database +- Production: Managed MySQL instance (e.g., PlanetScale, AWS RDS) +- Environment variables for database connection +- Separate environments for testing and production + +### Build Process + +1. Run database migrations (`prisma db push`) +2. Generate Prisma client +3. Build Next.js application +4. Generate service worker +5. Optimize and compress assets + +### Hosting + +- Deploy to Vercel (recommended for Next.js) +- Configure custom domain +- Set up SSL certificate +- Configure environment variables + +### Monitoring + +- Error tracking (e.g., Sentry) +- Performance monitoring +- Database query performance +- User analytics (optional) + +## Future Enhancements + +### Potential Features + +- Collaborative lists (multi-user) +- Recurring tasks +- Task dependencies +- Attachments and file uploads +- Calendar integration +- Notifications and reminders +- Dark mode +- Customizable themes +- Export/import functionality +- Task templates +- Subtasks and checklists +- Time tracking +- Analytics and insights + +### Technical Improvements + +- Real-time synchronization with WebSockets +- Backend API for mobile apps +- Advanced caching strategies +- Performance optimizations +- Accessibility improvements +- Internationalization (i18n) diff --git a/.kiro/specs/ultimate-lists-app/requirements.md b/.kiro/specs/ultimate-lists-app/requirements.md new file mode 100644 index 0000000..9a97856 --- /dev/null +++ b/.kiro/specs/ultimate-lists-app/requirements.md @@ -0,0 +1,114 @@ +# Requirements Document + +## Introduction + +The Ultimate Lists App is an enhanced todo list application designed to provide a superior task management experience beyond traditional todo apps. The system SHALL function as a Progressive Web App (PWA) that users can install on their iOS devices via Safari's "Add to Home Screen" feature, providing a native-like experience without requiring App Store distribution. The application SHALL offer advanced features for organizing, prioritizing, and managing tasks across multiple lists with rich metadata and flexible viewing options. + +## Glossary + +- **Ultimate Lists App**: The web-based task management system being specified +- **Task**: An individual item representing work to be completed, containing a description and optional metadata +- **List**: A collection of related tasks organized under a common theme or project +- **PWA (Progressive Web App)**: A web application that uses modern web capabilities to deliver an app-like experience +- **Service Worker**: A script that enables offline functionality and caching for PWAs +- **Web App Manifest**: A JSON file that provides metadata for PWA installation +- **Local Storage**: Browser-based persistent storage mechanism for data +- **Task Metadata**: Additional information about a task such as priority, due date, tags, or notes + +## Requirements + +### Requirement 1: Task Management + +**User Story:** As a user, I want to create, edit, and delete tasks with rich metadata, so that I can capture detailed information about my work items. + +#### Acceptance Criteria + +1. WHEN a user enters a task description and submits it, THE Ultimate Lists App SHALL create a new task with the provided description +2. WHEN a user edits a task, THE Ultimate Lists App SHALL update the task with the modified information and preserve the task's unique identifier +3. WHEN a user deletes a task, THE Ultimate Lists App SHALL remove the task from the list and update the display immediately +4. WHEN a user adds metadata to a task, THE Ultimate Lists App SHALL store the metadata and associate it with the task +5. WHEN a user marks a task as complete, THE Ultimate Lists App SHALL update the task status and visually distinguish it from incomplete tasks + +### Requirement 2: Multiple Lists + +**User Story:** As a user, I want to organize tasks into multiple lists, so that I can separate different projects or areas of my life. + +#### Acceptance Criteria + +1. WHEN a user creates a new list, THE Ultimate Lists App SHALL create the list with a unique identifier and user-provided name +2. WHEN a user views their lists, THE Ultimate Lists App SHALL display all created lists with their names and task counts +3. WHEN a user selects a list, THE Ultimate Lists App SHALL display only the tasks belonging to that list +4. WHEN a user deletes a list, THE Ultimate Lists App SHALL remove the list and all associated tasks +5. WHEN a user renames a list, THE Ultimate Lists App SHALL update the list name while preserving all associated tasks + +### Requirement 3: Data Persistence + +**User Story:** As a user, I want my tasks and lists to be saved automatically, so that I don't lose my data when I close the app. + +#### Acceptance Criteria + +1. WHEN a user creates or modifies a task, THE Ultimate Lists App SHALL persist the change to local storage immediately +2. WHEN a user creates or modifies a list, THE Ultimate Lists App SHALL persist the change to local storage immediately +3. WHEN a user reopens the application, THE Ultimate Lists App SHALL load all previously saved tasks and lists from local storage +4. WHEN data is persisted to local storage, THE Ultimate Lists App SHALL serialize the data in a consistent format +5. WHEN data is loaded from local storage, THE Ultimate Lists App SHALL deserialize the data and restore the application state + +### Requirement 4: Progressive Web App Capabilities + +**User Story:** As a user, I want to install the app on my iPhone home screen, so that I can access it quickly like a native app. + +#### Acceptance Criteria + +1. WHEN a user visits the application in Safari, THE Ultimate Lists App SHALL provide a web app manifest with appropriate metadata +2. WHEN a user adds the app to their home screen, THE Ultimate Lists App SHALL display with a custom icon and name +3. WHEN a user opens the installed app, THE Ultimate Lists App SHALL launch in standalone mode without browser UI +4. WHEN the application is offline, THE Ultimate Lists App SHALL continue to function using cached resources via a service worker +5. WHEN the application regains connectivity, THE Ultimate Lists App SHALL synchronize any cached changes if applicable + +### Requirement 5: Enhanced Task Features + +**User Story:** As a user, I want advanced task features like priorities, due dates, and tags, so that I can better organize and manage my work. + +#### Acceptance Criteria + +1. WHEN a user assigns a priority to a task, THE Ultimate Lists App SHALL store the priority level and allow filtering by priority +2. WHEN a user sets a due date on a task, THE Ultimate Lists App SHALL store the date and display it with the task +3. WHEN a user adds tags to a task, THE Ultimate Lists App SHALL store the tags and enable filtering tasks by tag +4. WHEN a user adds notes to a task, THE Ultimate Lists App SHALL store the notes and display them when viewing task details +5. WHEN a user views tasks with due dates, THE Ultimate Lists App SHALL visually indicate overdue tasks + +### Requirement 6: Task Sorting and Filtering + +**User Story:** As a user, I want to sort and filter my tasks in different ways, so that I can focus on what's most important. + +#### Acceptance Criteria + +1. WHEN a user selects a sort option, THE Ultimate Lists App SHALL reorder tasks according to the selected criteria +2. WHEN a user applies a filter, THE Ultimate Lists App SHALL display only tasks matching the filter criteria +3. WHEN a user combines multiple filters, THE Ultimate Lists App SHALL display tasks matching all active filter criteria +4. WHEN a user clears filters, THE Ultimate Lists App SHALL restore the full task list view +5. WHEN tasks are sorted by due date, THE Ultimate Lists App SHALL place tasks without due dates at the end of the list + +### Requirement 7: User Interface and Experience + +**User Story:** As a user, I want a clean, intuitive interface that works well on mobile devices, so that I can efficiently manage my tasks on the go. + +#### Acceptance Criteria + +1. WHEN a user interacts with the application on a mobile device, THE Ultimate Lists App SHALL provide touch-optimized controls with appropriate sizing +2. WHEN a user views the application, THE Ultimate Lists App SHALL display a responsive layout that adapts to different screen sizes +3. WHEN a user performs an action, THE Ultimate Lists App SHALL provide immediate visual feedback +4. WHEN a user navigates between views, THE Ultimate Lists App SHALL maintain smooth transitions and preserve scroll position where appropriate +5. WHEN the application displays lists and tasks, THE Ultimate Lists App SHALL use clear visual hierarchy and readable typography + +### Requirement 8: Task Search + +**User Story:** As a user, I want to search across all my tasks and lists, so that I can quickly find specific items. + +#### Acceptance Criteria + +1. WHEN a user enters a search query, THE Ultimate Lists App SHALL display tasks containing the query text in their description, notes, or tags +2. WHEN a user searches, THE Ultimate Lists App SHALL highlight matching text in search results +3. WHEN a user clears a search query, THE Ultimate Lists App SHALL restore the previous view +4. WHEN a user searches with no results, THE Ultimate Lists App SHALL display a message indicating no matches were found +5. WHEN a user performs a search, THE Ultimate Lists App SHALL search across all lists unless a specific list is selected diff --git a/.kiro/specs/ultimate-lists-app/tasks.md b/.kiro/specs/ultimate-lists-app/tasks.md index 6be9eb3..145629b 100644 --- a/.kiro/specs/ultimate-lists-app/tasks.md +++ b/.kiro/specs/ultimate-lists-app/tasks.md @@ -33,7 +33,7 @@ - **Property 10: List rename preserves items** - **Validates: Requirements 2.5** -- [ ] 3. Implement search functionality +- [x] 3. Implement search functionality - Create searchItems server action that searches across title, description, notes, and tags - Add support for list-scoped search vs global search - Implement text highlighting utility function @@ -51,7 +51,7 @@ - **Property 24: Search scope correctness** - **Validates: Requirements 8.5** -- [ ] 4. Implement filtering and sorting +- [x] 4. Implement filtering and sorting - Create getItemsByFilter server action with support for priority, completed, tags, and due date filters - Implement compound filter logic (AND conditions) - Add sorting by createdAt, dueDate, priority, and title @@ -74,7 +74,7 @@ - **Property 19: Null due date sorting** - **Validates: Requirements 6.5** -- [ ] 5. Create item detail page and form components +- [x] 5. Create item detail page and form components - Build /list/[id]/item/[itemId] page for viewing full item details - Create ItemForm component with fields for all metadata (priority, due date, tags, notes) - Create ItemDetail component to display all item information diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/app/list/[id]/item/[itemId]/edit/page.tsx b/app/list/[id]/item/[itemId]/edit/page.tsx new file mode 100644 index 0000000..c26fbc6 --- /dev/null +++ b/app/list/[id]/item/[itemId]/edit/page.tsx @@ -0,0 +1,41 @@ +import { notFound } from 'next/navigation' +import { getItem } from '@/lib/actions' +import ItemForm from '@/components/ItemForm' +import Link from 'next/link' +import { ArrowLeft } from 'lucide-react' + +interface PageProps { + params: { + id: string + itemId: string + } +} + +export default async function EditItemPage({ params }: PageProps) { + const item = await getItem(params.itemId) + + if (!item || item.listId !== params.id) { + notFound() + } + + return ( +
+ {/* Header */} +
+ + + Back + +

Edit Task

+
+ + {/* Form */} +
+ +
+
+ ) +} diff --git a/app/list/[id]/item/[itemId]/page.tsx b/app/list/[id]/item/[itemId]/page.tsx new file mode 100644 index 0000000..27a988b --- /dev/null +++ b/app/list/[id]/item/[itemId]/page.tsx @@ -0,0 +1,54 @@ +import { notFound } from 'next/navigation' +import { getItem } from '@/lib/actions' +import ItemDetail from '@/components/ItemDetail' +import Link from 'next/link' +import { ArrowLeft, Edit, Trash2 } from 'lucide-react' +import DeleteItemButton from '@/components/DeleteItemButton' +import ToggleItemButton from '@/components/ToggleItemButton' + +interface PageProps { + params: { + id: string + itemId: string + } +} + +export default async function ItemDetailPage({ params }: PageProps) { + const item = await getItem(params.itemId) + + if (!item || item.listId !== params.id) { + notFound() + } + + return ( +
+ {/* Header Actions */} +
+ + + Back to List + + +
+ + + + Edit + + +
+
+ + {/* Item Detail */} +
+ +
+
+ ) +} diff --git a/components/DeleteItemButton.tsx b/components/DeleteItemButton.tsx new file mode 100644 index 0000000..416c1c2 --- /dev/null +++ b/components/DeleteItemButton.tsx @@ -0,0 +1,60 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import { deleteItem } from '@/lib/actions' +import { Trash2 } from 'lucide-react' + +interface DeleteItemButtonProps { + itemId: string + listId: string +} + +export default function DeleteItemButton({ itemId, listId }: DeleteItemButtonProps) { + const router = useRouter() + const [isDeleting, setIsDeleting] = useState(false) + const [showConfirm, setShowConfirm] = useState(false) + + const handleDelete = async () => { + setIsDeleting(true) + try { + await deleteItem(itemId) + router.push(`/list/${listId}`) + } catch (error) { + console.error('Error deleting item:', error) + setIsDeleting(false) + setShowConfirm(false) + } + } + + if (showConfirm) { + return ( +
+ + +
+ ) + } + + return ( + + ) +} diff --git a/components/ItemDetail.tsx b/components/ItemDetail.tsx new file mode 100644 index 0000000..617e7a6 --- /dev/null +++ b/components/ItemDetail.tsx @@ -0,0 +1,163 @@ +import { Calendar, Tag, FileText, AlertCircle, CheckCircle2, Circle } from 'lucide-react' +import Link from 'next/link' + +interface ItemDetailProps { + item: { + id: string + title: string + description: string | null + completed: boolean + priority: string + dueDate: Date | null + tags: any + notes: string | null + createdAt: Date + updatedAt: Date + listId: string + } + listTitle: string +} + +export default function ItemDetail({ item, listTitle }: ItemDetailProps) { + const tags = Array.isArray(item.tags) ? item.tags : [] + const isOverdue = item.dueDate && new Date(item.dueDate) < new Date() && !item.completed + + const priorityColors = { + low: 'bg-green-100 text-green-800', + medium: 'bg-yellow-100 text-yellow-800', + high: 'bg-red-100 text-red-800', + } + + const priorityColor = priorityColors[item.priority as keyof typeof priorityColors] || priorityColors.medium + + return ( +
+ {/* Header */} +
+
+ + {listTitle} + + / + Task Details +
+
+
+ {item.completed ? ( + + ) : ( + + )} +
+
+

+ {item.title} +

+ {item.completed && ( +

Completed

+ )} +
+
+
+ + {/* Metadata Grid */} +
+ {/* Priority */} +
+
+ + Priority +
+ + {item.priority.charAt(0).toUpperCase() + item.priority.slice(1)} + +
+ + {/* Due Date */} +
+
+ + Due Date +
+ {item.dueDate ? ( +
+

+ {new Date(item.dueDate).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + })} +

+ {isOverdue && ( +

Overdue

+ )} +
+ ) : ( +

No due date

+ )} +
+
+ + {/* Description */} + {item.description && ( +
+

Description

+

{item.description}

+
+ )} + + {/* Tags */} + {tags.length > 0 && ( +
+
+ + Tags +
+
+ {tags.map((tag: string) => ( + + {tag} + + ))} +
+
+ )} + + {/* Notes */} + {item.notes && ( +
+
+ + Notes +
+

{item.notes}

+
+ )} + + {/* Timestamps */} +
+

+ Created: {new Date(item.createdAt).toLocaleString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + })} +

+

+ Updated: {new Date(item.updatedAt).toLocaleString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + })} +

+
+
+ ) +} diff --git a/components/ItemForm.tsx b/components/ItemForm.tsx new file mode 100644 index 0000000..af5a420 --- /dev/null +++ b/components/ItemForm.tsx @@ -0,0 +1,267 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import { updateItem, createItem } from '@/lib/actions' + +interface ItemFormProps { + listId: string + item?: { + id: string + title: string + description: string | null + priority: string + dueDate: Date | null + tags: any + notes: string | null + } + mode?: 'create' | 'edit' +} + +export default function ItemForm({ listId, item, mode = 'edit' }: ItemFormProps) { + const router = useRouter() + const [isSubmitting, setIsSubmitting] = useState(false) + const [errors, setErrors] = useState>({}) + + // Form state + const [title, setTitle] = useState(item?.title || '') + const [description, setDescription] = useState(item?.description || '') + const [priority, setPriority] = useState<'low' | 'medium' | 'high'>( + (item?.priority as 'low' | 'medium' | 'high') || 'medium' + ) + const [dueDate, setDueDate] = useState( + item?.dueDate ? new Date(item.dueDate).toISOString().split('T')[0] : '' + ) + const [tags, setTags] = useState( + Array.isArray(item?.tags) ? item.tags : [] + ) + const [tagInput, setTagInput] = useState('') + const [notes, setNotes] = useState(item?.notes || '') + + const validateForm = () => { + const newErrors: Record = {} + + if (!title.trim()) { + newErrors.title = 'Title is required' + } + + if (title.length > 200) { + newErrors.title = 'Title must be less than 200 characters' + } + + setErrors(newErrors) + return Object.keys(newErrors).length === 0 + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + + if (!validateForm()) { + return + } + + setIsSubmitting(true) + + try { + const data = { + title: title.trim(), + description: description.trim() || null, + priority, + dueDate: dueDate ? new Date(dueDate) : null, + tags, + notes: notes.trim() || null, + } + + if (mode === 'create') { + await createItem(listId, data.title, data.description || undefined, { + priority: data.priority, + dueDate: data.dueDate, + tags: data.tags, + notes: data.notes, + }) + router.push(`/list/${listId}`) + } else if (item) { + await updateItem(item.id, data) + router.push(`/list/${listId}/item/${item.id}`) + } + } catch (error) { + console.error('Error saving item:', error) + setErrors({ submit: 'Failed to save item. Please try again.' }) + } finally { + setIsSubmitting(false) + } + } + + const handleAddTag = () => { + const tag = tagInput.trim() + if (tag && !tags.includes(tag)) { + setTags([...tags, tag]) + setTagInput('') + } + } + + const handleRemoveTag = (tagToRemove: string) => { + setTags(tags.filter((tag) => tag !== tagToRemove)) + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault() + handleAddTag() + } + } + + return ( +
+ {/* Title */} +
+ + setTitle(e.target.value)} + className={`w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${ + errors.title ? 'border-red-500' : 'border-gray-300' + }`} + placeholder="Enter task title" + /> + {errors.title && ( +

{errors.title}

+ )} +
+ + {/* Description */} +
+ +